集合:容器,用一个对象管理多个对象;
集合VS 数组
数组:不能自动增长;只能存放同类型的元素
集合:能自动扩容;部分集合允许存放不同类型的元素;
Collection:父接口;
Set:接口 ---一个实现类: HashSet
List:接口---三个实现类: LinkedList,Vector,ArrayList
SortedSet:接口---实现类:TreeSet
List:有序列表,允许存放重复的元素;
实现类:
ArrayList:数组实现,查询快,增删慢,线程不安全,轻量级;下标也是从0开始;
LinkedList:链表实现,增删快,查询慢
Vector:数组实现,线程安全,重量级
Set: 无序集合,不允许存放重复的元素;
实现类
HashSet:equals返回true,hashCode返回相同的整数;哈希表;
SortedSet:对Set排序
实现类
TreeSet:二叉树实现的;
Iterator:接口,迭代器;java.util;hasNext();next();remove();
Iterable:可迭代的,访问的 ;java.lang;实现了可迭代的接口就可以用迭代的方式访问;
只需实现 iterator();方法即可;Iterator iterator();
模拟:
class ArrayList{
Iterator iterator(){
return new Iterator(){
public boolean hasNext(){}
public Object next(){}
...
};
}
}
三种循环的访问方式:
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
Iterator it=list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
for--each 循环:
for(Object obj:list){
System.out.println(obj);
}
只有实现了Iterable接口的才能用第三种;能用第二种的也一定能用第三种;
数组:优:访问快,操作效率高,
缺:插入删除效率低,长度不能变;
链表:跟数组相反
ArrayList:自动扩容,是数组照搬过来的;
========
HashSet:
========
构造方法:
HashSet(int initialCapacity)
通过Hash散列存储;希望元素均匀的分布在存储空间内;
Hash散列扩容并不是所有的空间都放满才申请新的空间;一般是75%的容量后就去扩容;
可以自己指定;10%就去扩容,保证了效率,浪费了空间;
Set放进去的顺序和取出来的顺序不一致;不允许有重复的元素;覆盖hashcode()方法;
Iterator it=set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
for(Object o:set){
System.out.println(o);
}
=================
ArrayList/LinkedList
=================
1、 用List实现一个栈;
2、 set是怎么实现的?
set.add("hello"); hash函数运算,()%16 -----0~15之间的值;
obj.hashcode();//Object对象的
HashSet,放到Hash散列里,有输入: 得到哈希码,obj.hashcode(),对16求余,有输出,放到相应的位置;
如果发生冲突,先判断要加进去的对象的equals(已经放进去的对象);是否相等;
如果obj1.equals(obj2)=true;那么舍弃obj2;
否则,调用冲突处理机制,将obj2放到另外一个位置;
两个Student对象的equals 相等;但是哈希码不相等,所以没有覆盖;
hs.add(obj);--->obj.hashcode();得到哈希码--->做运算得到下标--->看是否冲突:如果不冲突直接加
----> 如果冲突,调用equals方法,如果返回真,那么不加obj,否则找某个空位加进来;
Object里的hashcode()方法默认返回的是两个对象的地址;
String 类覆盖了hashcode()方法,人为制造冲突,它认为相同的串返回相同的hashcode;
只有要放到hash散列时才用到hashcode方法;
放到set里能保证对象不重复,自定义类要 覆盖hashcode方法;
覆盖hashcode方法原则:
1)、一定要让那些我们认为相同的对象返回相同的hashcode值;equals()相等;
public int hashcode(){ return 1; } //能达到要求,意味着把取舍权利完全交给equals;
能达到效果,但是效率太低;
2)、考虑效率,尽量让那些我们认为不相同的对象返回不同的hashcode值;
public int hashcode(){ return this.name.hashcode()+this.age; }
//只有name和age都相等才会返回相同的哈希码;避免了不必要的冲突;
减少了通过equals比较的次数,提高了效率;
3)、尽量的让对象的hashcode值散列开;即随机分布,均匀分布;
this.age+this.id;//并没有散列开;21~140
用属性(如果是整数直接用,否则取属性的hashcode())做 ^ 异或;相同为0,不同为1;比21~140范围要大;
toString方法默认返回的是 类名@地址 (如果覆盖了hashcode方法,那么输出的是hashcode方法的返回值)
day07.Student@1;
注:只覆盖hashcode不覆盖equals是无意义的;发生冲突后,
比equals时比的仍然是地址;hashcode是为hash散列服务的;
只覆盖equals我们要比较的是两个对象是否相等;并不放到hash散列中去;
===============
SortedSet:TreeSet
===============
有序:加载的顺序跟放的顺序一致
无序:加载的顺序跟放的顺序不一致
排好序:按照集合中值的大小排列;不需要手工写排序的代码;
原则:
1) 往TreeSet:中放的对象必须是同类型的;
2) 能够比较大小;定义比较规则;TreeSet 自己有比较的方法;
java.lang.Comparable //可比较的;
int compareTo(T o) //小的话返回负整数,相等返回0;大的话返回正整数;
public int compareTo(Object o){
Student s=(Student)o;
return this.age-s.age;
}
TreeSet中不允许出现CompareTo结果为0的两个对象;
String 覆盖了CompareTo方法,是 按字典顺序排列的;
Comparetor :java.util包下的;
java.lang包:
Iterable:可迭代的
Comparable:可比较的 compareTo()方法怎么覆盖
java.util包:
Iterator:迭代器
Comparetor:比较器
java.util包里的;
public class MyComparator implements Comparator{
public int compare(Object o1,Object o2){
Student s1=(Student)o1;
Student s2=(Student)o2;
return s2.getAge()-s1.getAge();//按从大到小排列;
}
}
在构造TreeSet时构造进去:
TreeSet ts=new TreeSet(new MyComparator());
如果在TreeSet中构造了比较器就用比较器的比较规则;
如果没有构造比较器就直接用自定义类中的CompareTo的比较规则;
=====
Map:
=====
HashMap:键值对,key不能重复,但是value可以重复;key的实现就是HashSet;value对应着放;
HashSet 的后台有一个HashMap;初始化后台容量;只不过生成一个HashSet的话,系统
只提供key的访问;
如果有两个Key重复,那么会覆盖之前的;
HashTable:线程安全的
Properties:java.util.Properties; key和value都是String类型,用来读配置文件;
两者区别:HashMap线程不安全的,允许null作为key或value;
HashTable线程安全的,不允许null作为key或value;
TreeMap: 对key排好序的Map; key 就是TreeSet, value对应每个key;
key要实现Comparable接口或TreeMap有自己的构造器;
HashSet:remove(Object o)的原则看这个对象O的Hashcode和equals是否相等,并不是看是不是一个对象;
定义一个Map; key是课程名称,value是Integer表示选课人数;
map.put(cou,map.get(cou)+new Integer(1));
(int)(Math.random()*cou.length);从0到要选的课程数减-1
本章学习方向:熟练使用API,记住常用的几个容器及其特点
===================================
扩展:容器中装容器
每个Collecton 都有一个addAll()方法,可以把一个集合完全装载到另外一个集合里面:例如:
Set set=new Set();
List ls=new List();
Ls.addAll(set);//把Set装载在list里面。
====================================
面试经验(知识点):
java.util.stack(stack即为堆栈)的父类为Vector。可是stack的父类是最不应该为Vector的。因为Vector的底层是数组,且Vector有get方法(意味着它可能访问到并不属于最后一个位置元素的其他元素,很不安全)。
对于堆栈和队列只能用push类和get类。
Stack类以后不要轻易使用。
!!!实现堆栈一定要用LinkedList。
(在JAVA1.5中,collection有queue来实现队列。)
====================================
注:HashMap底层也是用数组,HashSet底层实际上也是HashMap,HashSet类中有HashMap属性(我们如何在API中查属性)。HashSet实际上为(key.null)类型的HashMap。有key值而没有value值。
正因为以上的原因,TreeSet和TreeMap的实现也有些类似的关系。
注意:TreeSet和TreeMap非常的消耗时间,因此很少使用。
我们应该熟悉各种实现类的选择——非常体现你的功底。
HashSet VS TreeSet:HashSet非常的消耗空间,TreeSet因为有排序功能,因此资源消耗非常的高,我们应该尽量少使用,而且最好不要重复使用。
基于以上原因,我们尽可能的运用HashSet而不用TreeSet,除非必须排序。
同理:HashMap VS TreeMap:一般使用HashMap,排序的时候使用TreeMap。
HashMap VS Hashtable(注意在这里table的第一个字母小写)之间的区别有些类似于ArrayList和Vector,Hashtable是重量级的组件,在考虑并发的情况,对安全性要求比较高的时候使用。
Map的运用非常的多。
==========================================
组合复用原则:
*白盒复用和黑盒复用
1.白盒复用:又叫继承复用,从某种程度上说白盒复用破坏了封装。
例:class Zhangsw{
public void teachCpp(){
System.out.println("Teach Cpp");
}
public void chimogu(){
}
}
class Xiaozr extends Zhangsw{
}
2.黑盒复用:又叫组合复用。
例:class Zhangsw{
public void teachCpp(){
System.out.println("Teach Cpp");
}
public void chimogu(){
}
}
class Xiaozr {
private Zhangsw zhangsw = new Zhangsw();
public void teachCpp(){
zhangsw.teachCpp();
}
}
3.原则:组合复用取代继承复用。
柴油发电机
发电机
柴油机
柴油发电机
13636374743(上海)
13291526067(嘉兴)