整理:JAVA集合框架

taocr' note: JAVA集合框架.mmap

参考 Collection , List , Set 和 Map 用法和区别:

http://blog.csdn.net/zccst/article/details/5056920

参考 Java8系列之重新认识HashMap:

http://www.importnew.com/20386.html

HashMap和Hashtable的区别?

https://www.zhihu.com/question/20581065

Java HashMap工作原理及实现

http://yikun.github.io/2015/04/01/Java-HashMap工作原理及实现/

Java 集合系列14之 Map总结(HashMap, Hashtable, TreeMap, WeakHashMap等使用场景)

http://www.cnblogs.com/skywang12345/p/3311126.html

数组、ArrayList、List、LinkedList的区别

http://www.cnblogs.com/janneystory/p/5758958.html

Java并发编程:并发容器之CopyOnWriteArrayList(转载)

http://www.cnblogs.com/dolphin0520/p/3938914.html

 java 常用集合list与Set、Map区别及适用场景总结

http://blog.csdn.net/qq_22118507/article/details/51576319

数组的存储空间是连续的,占用内存严重,故空间复杂度很大,但是查找时间复杂度较小。数组的特点:寻址容易,插入和删除困难。

链表的存储空间是离散的,占用内存比较宽松,故空间复杂度较小,但查找时间复杂度较大。链表的特点:寻址困难,插入和删除容易。

Iterable<E>

├— Collection<E>  接口

               ├— List<E>

                         ├— Queue<E>

                         ├— Set<E>

                         ├— SortedSet<E>

Map<K,V> 接口

          ├—SortedMap<K,V>

List 是有序的 Collection ,次序是 List 最重要的特点:它保证维护元素特定的顺序。

Set 是一种不包含重复的元素的 Collection ,加入 Set 的元素必须定义 equals() 方法以确保对象的唯一性 ( 即任意的两个元素 e1 和 e2都有 e1.equals(e2)=false ),与 List 不同的是, Set 接口不保证维护元素的次序。最后, Set 最多有一个 null 元素。

Set不能按照索引随机访问元素,这是它与List的一个重要区别。

Map 没有继承 Collection 接口, Map 提供 key 到 value 的映射,你可以通过“键”查找“值”。一个Map 中不能包含相同的 key ,每个 key 只能映射一个 value 。 Map 接口提供 3 种集合的视图, Map 的内容可以被当作一组 key 集合,一组 value 集合,或者一组 key-value 映射。

list与Set、Map区别及适用场景

1、List,Set都是继承自Collection接口,Map则不是

2、

List特点:元素有放入顺序,元素可重复 ,

Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。)

3.Set和List对比:

Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。

List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。

4.Map适合储存键值对的数据

5.线程安全集合类与非线程安全集合类

LinkedList、ArrayList、HashSet是非线程安全的,Vector是线程安全的;

HashMap是非线程安全的,HashTable是线程安全的;

StringBuilder是非线程安全的,StringBuffer是线程安全的。

1 MAP

Map接口                                        put() get() remove()                                  键值对的集合

     |

Directionary父类

          ├—Hashtable                                                     同步、线程安全           数组+链表(链地址法)

AbstractMap父类

          ├—HashMap                        以下均线程不安全  允许null值键/值          数组链表/红黑树

          │    LinkedHashMap          保存插入顺序                                             HashMap + 双向链表

          │     WeakHashMap            对 key 实行“弱引用”                              数组+链表

          ├TreeMap                           基于红黑树、结果是经过排序的                红黑树

          ├IdentifyHashMap             使用 == 代替equals()对“键”作比较      数组+链表

          └ConcurrentHashMap      线程安全      读取无锁、写入分段锁         数组+链表/红黑树

          └ ConcurrentSkipListMap  线程安全  适合大规模数据访问 有序       跳表

          └ EnumMap                           Key为Enum类型                                       数组

1.1 Hashtable

HashTable的key和value都不允许为null值

HashTable使用Enumeration

HashTable直接使用对象的hashCode

HashTable中构造hash数组时initialCapacity默认大小是11,增加的方式是 old*2+1。

Hashtable是遗留类,需要线程安全的场合可以用ConcurrentHashMap替换。

HashTable的方法使用synchronized,所以是线程安全的,但是性能差。

Hash算法:

key的hashCode()

1.2 HashMap

HashMap允许null值键/值

HashMap中构造hash数组时initialCapacity默认大小是16,而且一定是2的指数。

HashMap在JAVA8中一个链表超过8个元素时使用红黑树代替链表,查找复杂度变为O(logN)

HashMap使用Iterator

HashMap重新计算hash值,而且用与代替求模

HashMap线程不安全

查找复杂度:

当Hash算法较好,碰撞少时,O(1)

当碰撞较多,使用链表辅助度O(n),使用红黑树O(lgn)

Hash算法:

通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16)

1.3 TreeMap

TreeMap实现SortedMap接口,能够把它保存的记录根据键排序

TreeMap 当用Iterator遍历TreeMap时,得到的记录是排过序的。

TreeMap时,key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator。

TreeMap 使用Comparator比较键是否相同。

实验发现 HashMap必须要 hashcode 和 equals 都相同,才属于相同的键。

查找/插入复杂度:

平均O(logn)

1.4 ConcurrentHashMap

参考 ConcurrentHashMap和HashMap :

https://netboyc.gitbooks.io/java-high/content/concurrenthashmaphe_hashmap.html

线程安全,读取无锁。

ConcurrentHashMap的实现方式---锁桶(或段)。ConcurrentHashMap将hash表分为16个桶(默认值),诸如get,put,remove等常用操作只锁当前需要用到的桶。试想,原来只能一个线程进入,现在却能同时16个写线程进入(写线程才需要锁定,而读线程几乎不受限制,之后会提到),并发性的提升是显而易见的。

参考 java中ConcurrentHashMap的使用及在Java 8中的冲突方案:

https://segmentfault.com/a/1190000006811416

CHM适用于读者数量超过写者时,当写者数量大于等于读者时,CHM的性能是低于Hashtable和synchronized Map的。

参考 java8中对ConcurrentHashMap的改进:

http://blog.csdn.net/wangxiaotongfan/article/details/52074160

改进一:取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存数据,采用table数组元素作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率。

改进二:将原先table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构。

问题:

 ConcurrentHashMap、synchronized与线程安全

http://blog.csdn.net/sadfishsc/article/details/42394955

文章中的问题,在于containsKey和put两个操作不是原子的,不是整体同步的

参考:AtomicLongMap的使用

http://chenjumin.iteye.com/blog/2182482

使用锁来解决语句块整体问题,它使用ReentrantReadWriteLock读写锁

这个AtomicLongMap应该就是一个存储AtomicLong的ConcurrentHashMap(可能是Hash)

AtomicLongMap是Google Guava项目的一个类,它是线程安全、支持并发访问的。

ReentrantReadWriteLock会使用两把锁来解决问题,一个读锁,一个写锁

线程进入读锁的前提条件:

  没有其他线程的写锁,

  没有写请求或者有写请求,但调用线程和持有锁的线程是同一个

线程进入写锁的前提条件:

没有其他线程的读锁

没有其他线程的写锁

<span style="background-color: rgb(255, 255, 255); font-size: 15px;"--<------<span style="background-color: rgb(255, 255, 255); font-size: 15px;"--<------<span style="background-color: rgb(255, 255, 255); font-size: 15px;"--<------<span style="background-color: rgb(255, 255, 255); font-size: 15px;"--<--------------

2 List

List接口                          add() get() remove()

     |

AbstractList父类

          ├—ArrayList               线程不安全                              数组

          ├—LinkedList             线程不安全                              双向循环链表

          ├— Vector                  线程安全、效率低                   数组

                    ├— Stack

Object父类

          ├— CopyOnWriteArrayList     线程安全

2.1 ArrayList

ArrayList内部实现采用动态数组,当容量不够时,自动扩容至(当前容量1.5+1)。

元素的顺序按照插入的顺序排列。默认初始容量为10。

随机访问效率高,随机插入、删除效率低。

ArrayList是非线程安全的。

get() 直接读取第几个下标,复杂度 O(1)

add(E) 添加元素,直接在后面添加,复杂度O(1)

add(index, E) 添加元素,在第几个元素后面插入,后面的元素需要向后移动,复杂度O(n)

remove()删除元素,后面的元素需要逐个移动,复杂度O(n)

2.2 LinkedList

LinkedList内部使用双向链表实现。

随机访问效率低,随机插入、删除效率高。

可以当作堆栈、队列、双向队列来使用。

LinkedList也是非线程安全的。

get(index) 获取第几个元素,依次遍历,复杂度O(n)

add(E) 添加到末尾,复杂度O(1)

add(index, E) 添加第几个元素后,需要先查找到第几个元素,直接指针指向操作,复杂度O(n)

remove(index) 删除元素,直接指针指向操作,复杂度O(1)

2.3 Vector

Vector跟ArrayList是类似的,内部实现也是动态数组,随机访问效率高。

Vector是线程安全的。

2.4 Stack

Stack是栈,继承于Vector,其各种操作也是基于Vector的各种操作,因此其内部实现也是动态数组。

先进后出。

Stack是线程安全的。

2.5 CopyOnWriteArrayList

CopyOnWriteArrayList在java的并发场景中用得其实并不是非常多,因为它并不能完全保证读取数据的正确性。

CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完再将原容器的引用指向新的容器。

这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

CopyOnWrite容器存在两个问题,即内存占用问题和数据一致性问题。

CopyOnWrite并发容器用于读多写少的并发场景。比如白名单,黑名单,商品类目的访问和更新场景。

内存占用问题:压缩元素或换其他并发容器

数据一致性问题:换其他容器

- 它最适合于具有以下特征的应用程序:set 大小通常保持很小,只读操作远多于可变操作,需要在遍历期间防止线程间的冲突。

- 它是线程安全的。

- 因为通常需要复制整个基础数组,所以可变操作(add、set 和 remove 等等)的开销很大。 迭代器不支持可变 remove操作。

- 使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。

2.6 List使用场景

对于需要快速插入、删除元素,应该使用LinkedList

对于需要快速随机访问元素,应该使用ArrayList

如果List需要被多线程操作,应该使用Vector,如果只会被单线程操作,应该使用ArrayList

ArrayList等是非线程安全的,即它没有同步,不过,可以通过Collections.synchronizedList()静态方法返回一个同步的实例,如:

List synList = Collections.synchronizedList(list);

<span style="background-color: rgb(255, 255, 255); font-size: 14px; font-family: Arial;"--<-------------<span style="background-color: rgb(255, 255, 255); font-size: 14px; font-family: Arial;"--<-------------<span style="background-color: rgb(255, 255, 255); font-size: 14px; font-family: Arial;"--<-------------<span style="background-color: rgb(255, 255, 255); font-size: 14px; font-family: Arial;"--<-------------<span style="background-color: rgb(255, 255, 255); font-size: 14px; font-family: Arial;"--<-------------<span style="background-color: rgb(255, 255, 255); font-size: 14px; font-family: Arial;"--<-------------

3 Set

Set接口               add()  iterator()  remove()

     |

AbstractSet父类

               ├— TreeSet                                 有序 继承SortedSet            基于TreeMap

               ├— HashSet                                无序                                       基于HashMap

                          ├—  LinkedHashSet        有序                                       基于LinkedHashMap

               ├— CopyOnWriteArraySet          无序 线程安全                    基于CopyOnWriteArrayList

               ├— ConcurrentSkipListSet          有序 线程安全                    基于ConcurrentSkipListMap

               ├— EnumSet                                Abstract class                      位数组

                          ├—  RegularEnumSet      私有实现                              一个long存储 64bit

                          ├—  JumboEnumSet        私有实现                              可存储超过64bit

注意: HashSet

1.HashSet先调用HashCode方法算出Hash值,对比Hash值.相同进行第二步,建议重写HashCode方法

2.调用对象的Equals方法,建议重写Equals方法

注意:TreeSet

1.元素具备自然排序的特点,就按照自然顺序进行排序

2.如果元素不具备自然排序的特点,那么元素需要实现Comparable接口,自定义比较规则,重写CompareTo方法

3.1 Set使用场景

HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。为快速查找而设计的Set,我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。

CopyOnWriteArraySet用于读多写少的并发场景

ConcurrentSkipListSet适用于高并发的场景

发表评论

邮箱地址不会被公开。 必填项已用*标注