- list, map,set
- 接口:Collection
list, map,set
java.util包下面的所有的集合类都是快速失败fail-fast迭代器的,而java.util.concurrent包下面的所有的类都是安全失败fail-safe迭代器的。快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常。因为安全失败是基于之前的数据进行一份拷贝,所以不受源集合的影响。
List
- LinkedList 类
特点:对顺序访问进行了优化,向 List 中间插入与删除的开销并不大。随机访问则相对较慢
Foreach其实就是在使用迭代器,Iterator中的next()方法采用的是顺序访问方法,因此在LinkedList里使用Iterator较快 - ArrayList 类
ArrayList是实现了基于动态数组的数据结构,随机访问快.
在ArrayList中是Foreach比迭代器快。
上述所提到的ConcurrentModficationException是在使用迭代器的时候对集合进行修改才会抛异常,在你迭代之前,迭代器已经被通过list.itertor()创建出来了,如果在迭代的过程中,又对list进行了改变其容器大小的操作,那么Java就会给出异常。但是迭代器在遍历的时候是可以对元素进行删除的,因为迭代器中remove()函数是同步了modCount 与expectedModCount所以不会抛异常
如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList, 这个是concurrent包下的。如果在使用迭代器 遍历list的时候修改list则会报错。使用迭代器更加线程安全,因为它可以确保,在当前遍历的集合元素被更改的时候,它会抛出ConcurrentModificationException - Vector 类 与ArrayList相比是线程安全
Stack继承自Vector, 所有pop(),peek(),search()都加了synchronized关键字。search()源自vector的lastIndexOf()它会从尾到头扫描,如果不加同步方法,在多线程环境中有很多个这样的查询操作将会导致性能变差。
- LinkedList 类
Queue
1)阻塞式队列(BlockingQueue)队列满了以后再插入元素则会抛出异常,主要包括ArrayBlockQueue、PriorityBlockingQueue,
LinkedBlockingQueue。
2)双端队列Deque:支持在头、尾两端插入和移除元素,主要包括:ArrayDeque、LinkedBlockingDeque、LinkedList。Set
- HashSet 类 为快速查找设计的 Set
- LinkedHashSet 类:具有 HashSet 的查询速度,且内部使用链表维护元素的顺序 ( 插入的次序 ) 。于是在使用迭代器遍历 Set 时,结果会按元素插入的次序显示
- TreeSet 类保存次序的 Set, 底层为树结构。使用它可以从 Set 中提取有序的序列。
Map
- Map 也有内置的排序,因而不关心元素添加的顺序。如果添加元素的顺序对你很重要,应该使用 LinkedHashSet 或者 LinkedHashMap.
- HashMap 就是使用对象的 hashCode() 进行快速查询的。此方法能够显著提高性能。注意key要是不可变对象,否则有可能改变hashcode。当我们通过传递key调用get方法时,它再次使用hashCode()来找到数组中的索引,然后使用equals()方法找出正确的Entry,然后返回它的值。
HashMap的底层实现主要是借助数组+链表+红黑树来实现。Vector、HashTable等集合类效率比较低但都是线程安全的。
包java.util.concurrent下包含了大量线程安全的集合类,效率上有较大提升。
put操作:首先通过hashCode获得对应的三列桶,其次通过遍历看是否equals获得链地址上有无此元素,有则替换,无则增加到头的那个地方,null总是在最前面,而且仅仅保留一个null。
get操作:类似。因为get的时候会判断hashcode是否相同,然后equals是否,所以必须要设置key为不可变。
rehash过程 Entry[]的长度一定后,随着map里面数据的越来越长,超过了加载因子,必须调整table的大小。每次将数组拓展到原来数据长度的两倍。
解决hash冲突:再hash,链地址发,开放地址法. - ConcurrentHashMap 高并发的版本不像hashtable加锁影响性能。通过将hashmap分成很多片小的segment,每个segment都枷锁,来提高性能。 LinkedHashMap,TreeMap类似
- WeakHashMap 表示弱键映射,允许释放映射所指向的对象。这是为了解决某类特殊问题而设计的,如果映射之外没有引用指向某个“键”,则“键”可以被垃圾收集器回收。
- IdentityHashMap 使用==代替equals() 对“键”进行比较的散列映射。专为解决特殊问题而设计。
接口:Collection
Collection有一个重要的方法:iterator(),返回一个Iterator(迭代器),用于遍历集合的所有元素,我们不需要关心到底是哪种Iterator,它只需要获得这个Iterator接口即可,这就是接口的好处,面向对象的威力。
Iterator接口提供遍历任何Collection的接口。我们可以从一个Collection中使用迭代器方法来获取迭代器实例。迭代器取代了Java集合框架中的Enumeration。迭代器允许调用者在迭代过程中移除元素。
对于并发控制而言,锁是一种悲观策略,会阻塞线程执行,而无锁是一种乐观策略
HashMap是非线程安全的,在多线程环境还是需要用到hashtable,concurrenthashmap或者打包成同步map类,这其中concurrenthashmap性能最佳,但是其本质用到了互斥锁,或多或少会影响性能,或者造成死锁. 这时候可以考虑无锁,用到了CAS compareAndSwap,比如链表的插入操作,在插入之前先检查前节点是否指向它的下一个节点(考虑到多线程的影响),具体是用for循环做一个compareAndSwap的方法直到满足为止.这就是atomic原子性操作,但是这种方式只能保证一个变量保持这种原子性,多个的话就要另想它法.
应用:
jdk中
java.util.concurrent.atomic包,比如AtomicInterger这种都是用的CAS技术,无锁队列java.util.concurrent.ConcurrentLinkedQueue,跳跃表java.util.concurrent.ConcurrentSkipListMap.
jvm中
单线程环境中可以通过直接查看空闲表去分配,而多线程环境中,由于再给一个对象分配内存的时候不是原子性的操作,至少需要以下几步:查找空闲列表、分配内存、修改空闲列表等等,这是不安全的.虚拟机采用CAS配合上失败重试的方式保证更新操作的原子性.
多线程环境更好的策略是:每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲区(TLAB),线程内部需要分配内存时直接在TLAB上分配就行,避免了线程冲突。只有当缓冲区的内存用光需要重新分配内存的时候才会进行CAS操作分配更大的内存空间。
Comparable & Comparator
Collections中有两种方法实现sort(稳定排序 基数排序),
public static <T extends Comparable<? super T>> void sort(List<T> list)
方法某个类实现Comparable接口,
public static <T> void sort(List<T> list, Comparator<? super T> c)
需要一个Comparator比较器。