Java集合:Collection(List,Set,Queue);Map;Iterator(依附于Collection对象,主要用于遍历Collection集合中的元素)
Set:HashSet,LinkedHashSet,TreeSet,EnumSet(都是线程不安全的)
- HashSet:除了排序外都比TreeSet要好,默认大小是16
- LinkedHashSet:HashSet的子类,使用链表维护元素的插入顺序(如果没有重写compareTo方法),遍历比HashSet快,其他比HashSet慢(链表开销)
- TreeSet:调用compareTo方法使元素按升序排列(红黑树算法),compareTo返回0则认为相等
- EnumSet:性能最好,但只能保存同一个枚举类的枚举值作为集合元素
List:ArrayList,LinkedList,Vector
- ArrayList:数组形式实现,随机访问(get)性能好
- LinkedList:链表形式实现,迭代器遍历,插入删除性能好,此外还提供了双端队列和栈的功能
Vector:以数组形式实现,线程安全但很少用
ArrayList和Vector的区别(它们都是基于数组实现):
- ArrayList在内存不够时默认是old*1.5 + 1个,Vector是默认扩展1倍,默认大小都为10
- Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为线程安全需要更大的系统开销,它和hashtable一样,属于很古老的集合
Map:HashMap,LinkedHashMap,HashTable,TreeMap,EnumMap
- HashMap:性能比HashTable好,线程不安全,(判断key是否一样通过equals()和hashCode),推荐使用,底层由数组和链表实现,当一个map填满了75%(负载因子)的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中;JDK1.8中如果链表长度大于8会把链表转化为红黑树。
- HashTable:线程安全,不允许加入null,(判断key是否一样通过equals()和hashCode)
- LinkedHashMap:HashMap的子类,使用双向链表维护元素的插入顺序,性能略低于HashMap,迭代遍历快
- TreeMap:调用compareTo方法使key按升序排列,compareTo返回0则认为key相等
- EnumMap:性能最好,但只能保存同一个枚举类的枚举值作为key
ConcurrentHashMap:线程安全,原子操作,比hashtable效率高,对数据分块加锁
当多线程对ConcurrentHashMap 操作时,不是完全锁住map, 而是锁住相应的segment 。这样提高了并发效率。缺点:当遍历ConcurrentMap中的元素时,需要获取所有的segment 的锁,使用遍历时慢。锁的增多,占用了系统的资源。使得对整个集合进行操作的一些方法(例如 size() 或 isEmpty() )的实现更加困难,因为这些方法要求一次获得许多的锁,并且还存在返回不正确的结果的风险。HashMap和Hashtable的区别(它们都是基于数组和链表实现):
- Hashtable的方法是同步的(加了synchronized),HashMap则是非同步的,所以在多线程场合要手动同步HashMap(Collections的synchronizedMap()或者ConcurrentHashMap),这个区别就像Vector和ArrayList一样。
- Hashtable不允许null值(key和value都不可以),HashMap允许null值(key和value都可以)。
- Hashtable使用Enumeration和Iterator遍历,HashMap使用Iterator遍历。
- Hashtable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,增加方式是翻倍。
注:我们事先已知集合的使用场景,知道集合的大概范围,我们最好是指定初始化容量,这样对资源的利用会更加好,尤其是大数据量的前提下,效率的提升和资源的利用会显得更加具有优势。
foreach,iterator ,for循环
要使用foreach语法,必须是数组,或者该类必须实现Iterable接口才可以,语法如下:
for(T t : Iterable) { .... }
如果查看编译后的字节码,会发现foreach最终被编译器转为对iterator.next()的调用,这些jdk给隐藏起来了。迭代器这些方法的具体实现,各个集合类实现都不一样,有自己的方法。如ArrayList是数组的存储,循环的是数组的方法,而LinkedList是链表,循环的是链表的方法。
关于foreach和iterator的效率问题,其实他们的实现的过程是一样的。 如下面代码:
List list = new ArrayList();
for (Object obj : list) {
System.out.println(obj);
}
相当于
List list = new ArrayList();
Object obj;
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
obj = iterator.next();
System.out.println(obj);
}
for循环与for-each循环效率:
这个要从具体实现的迭代器来看,如ArrayList由于是数组的存储方式,直接使用for循环的get效率会更快,LinkedList是链式存储机制,所以使用foreach的迭代会高效很多。 写代码使用foreach语法是首选,代码更优美,也不容易出错。
另外关于foreach删除list的元素出错可参考 for each删除List中的元素出错