一、集合简介
1.1集合定义
是容器,存储任意数量的具有共同属性的对象
1.2集合作用
(1)在类的内部,对数据进行组织;
(2)简单而快速的搜索大数量的条目;
(3)有的集合接口,提供了一系列排列有序的元素,并且可以在序列中间快速的插入或者删除元素;
(4)有的集合接口,提供了映射关系,可以通过关键字(key)快速的查找到对应的唯一对象,而这个关键字可以是任意类型。
1.3集合体系图
二、集合概述
2.1 Collection接口的方法
boolean add(Object o):向集合中加入一个对象的引用
void clear():删除集合中所有的对象,即不再持有这些对象的引用
boolean isEmpty():判断集合是否为空
boolean contains(Object o): 判断集合中是否持有特定对象的引用
Iterartor iterator():返回一个Iterator对象,可以用来遍历集合中的元素
boolean remove(Object o):从集合中删除一个对象的引用
int size():返回集合中元素的数目
Object[] toArray(): 返回一个数组,该数组中包括集合中的所有元素
关于:Iterator()和toArray() 方法都用于集合的所有的元素,前者返回一个Iterator对象,后者返回一个包含集合中所有元素的数组。
Collection没有get()方法来取得某个元素。只能通过iterator()遍历元素。
2.2 Iterator接口声明了如下方法
hasNext():判断集合中元素是否遍历完毕,如果没有,就返回true
next():返回下一个元素
remove():从集合中删除上一个有next()方法返回的元素。
Set(集合): Set是最简单的一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。 Set接口主要实现了两个实现类:
HashSet: HashSet类按照哈希算法来存取集合中的对象,存取速度比较快
TreeSet:TreeSet类实现了SortedSet接口,能够对集合中的对象进行排序。
2.3 List的功能方法
实际上有两种List:一种是基本的ArrayList,其优点在于随机访问元素,另一种是更强大的LinkedList,它并不是为快速随机访问设计的,而是具有一套更通用的方法。
List:次序是List最重要的特点:它保证维护元素特定的顺序。List为Collection添加了许多方法,使得能够向List中间插入与移除元素(这只推 荐LinkedList使用。)一个List可以生成ListIterator,使用它可以从两个方向遍历List,也可以从List中间插入和移除元素。
ArrayList:由数组实现的List。允许对元素进行快速随机访问,但是向List中间插入与移除元素的速度很慢。ListIterator只应该用来由后向前遍历 ArrayList,而不是用来插入和移除元素。因为那比LinkedList开销要大很多。
LinkedList:对顺序访问进行了优化,向List中间插入与删除的开销并不大。随机访问则相对较慢。(使用ArrayList代替。)还具有下列方 法:addFirst(), addLast(), getFirst(), getLast(), removeFirst() 和 removeLast(), 这些方法 (没有在任何接口或基类中定义过)使得LinkedList可以当作堆栈、队列和双向队列使用。
2.4 Set的功能方法
Set具有与Collection完全一样的接口,因此没有任何额外的功能。实际上Set就是Collection,只 是行为不同。这是继承与多态思想的典型应用:表现不同的行为。Set不保存重复的元素(至于如何判断元素相同则较为负责)
Set :存入Set的每个元素都必须是唯一的,因为Set不保存重复元素。加入Set的元素必须定义equals()方法以确保对象的唯一性。Set与Collection有完全一样的接口。Set接口不保证维护元素的次序。
HashSet:为快速查找设计的Set。存入HashSet的对象必须定义hashCode()。
TreeSet: 保存次序的Set, 底层为树结构。使用它可以从Set中提取有序的序列。
LinkedHashSet:具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序)。于是在使用迭代器遍历Set时,结果会按元素插入的次序显示。
2.5 Map的功能方法
方法put(Object key, Object value)添加一个“值”(想要得东西)和与“值”相关联的“键”(key)(使用它来查找)。方法get(Object key)返回与给定“键”相关联的“值”。可以用containsKey()和containsValue()测试Map中是否包含某个“键”或“值”。 标准的Java类库中包含了几种不同的Map:HashMap, TreeMap, LinkedHashMap, WeakHashMap, IdentityHashMap。它们都有同样的基本接口Map,但是行为、效率、排序策略、保存对象的生命周期和判定“键”等价的策略等各不相同。
执行效率是Map的一个大问题。看看get()要做哪些事,就会明白为什么在ArrayList中搜索“键”是相当慢的。而这正是HashMap提高速 度的地方。HashMap使用了特殊的值,称为“散列码”(hash code),来取代对键的缓慢搜索。“散列码”是“相对唯一”用以代表对象的int值,它是通过将该对象的某些信息进行转换而生成的。所有Java对象都 能产生散列码,因为hashCode()是定义在基类Object中的方法。
HashMap就是使用对象的hashCode()进行快速查询的。此方法能够显着提高性能。
Map :维护“键值对”的关联性,使你可以通过“键”查找“值”
HashMap:Map基于散列表的实现。插入和查询“键值对”的开销是固定的。可以通过构造器设置容量capacity和负载因子load factor,以调整容器的性能。
LinkedHashMap: 类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点。而在迭代访问时发而更快,因为它使用链表维护内部次序。
TreeMap: 基于红黑树数据结构的实现。查看“键”或“键值对”时,它们会被排序(次序由Comparabel或Comparator决定)。TreeMap的特点在 于,你得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树。
WeakHashMap:弱键(weak key)Map,Map中使用的对象也被允许释放: 这是为解决特殊问题设计的。如果没有map之外的引用指向某个“键”,则此“键”可以被垃圾收集器回收。
IdentifyHashMap: : 使用==代替equals()对“键”作比较的hash map。专为解决特殊问题而设计。
三、常见比较
3.1 List、Set和Map的区别
List:
(1)采用线性结构存储,元素可以为null。
(2)和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
Set:
(1)采用离散存储方式,不允许存储值为null的元素且存储的元素不能重复
(2)检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
Map是键值对的存储方式
2.2 HashMap和ConcurrentHashMap
2.3 HashMap和Hashtable的区别
[if !supportLists](1) [endif]两者最主要的区别在于Hashtable是线程安全,而HashMap则非线程安全
[if !supportLists](2) [endif]HashMap可以使用null作为key,而Hashtable则不允许null作为key
[if !supportLists](3) [endif]HashMap是对Map接口的实现,HashTable实现了Map接口和Dictionary抽象类
[if !supportLists](4) [endif]HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75
[if !supportLists](5) [endif]两者计算hash的方法不同:Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取模,HashMap计算hash对key的hashcode进行了二次hash,以获得更好的散列值,然后对table数组长度取摸
2.4 HashSet和HashMap、Hashtable的区别
除开HashMap和Hashtable外,还有一个hash集合HashSet,有所区别的是HashSet不是key value结构,仅仅是存储不重复的元素,相当于简化版的HashMap,只是包含HashMap中的key而已
2.5 ArrayList、LinkedList、Vector的区别
Arraylist和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加插入元素,都允许直接序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以插入数据慢,查找有下标,所以查询数据快,Vector由于使用了synchronized方法-线程安全,所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项前后项即可,插入数据较快。
2.6 HashSet与Treeset的适用场景
(1)TreeSet 是二差树(红黑树的树据结构)实现的,Treeset中的数据是自动排好序的,不允许放入null值
(2)HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束
(3)HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例。
适用场景分析:HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。为快速查找而设计的Set,我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。
三、源码分析
3.1源码分析之ArrayList
(1)ArrayList初始化大小是 10
(2)扩容点规则是,新增的时候发现容量不够用了,就去扩容 (3)扩容大小规则是,扩容后的大小=原始大小+原始大小/2 + 1
3.2源码分析之LinkedList
LinkedList是一个双向链表,没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增就好
(1)LinkedList以链表的形式存储数据、对增删元素有很高的效率、查询效率较低、尤其是随机访问、效率不忍直视
(2)LinkedList继承AbstractSequentialdList(其继承与AbstractList、所以要求其子类要实现通过索引操作元素)、使得LinkedList支持使用索引
的“增删改查”操作
(3)LinkedList直接实现了List接口、使其可以内部存储元素有序并且为每个元素提供索引
(4)LinkedList直接实现了Deque接口、Deque接口继承了Queue、使其可以作为双向链表这种数据结构来使用、操作元素、
(5)LinkedList直接实现了Cloneable接口、使其可以复制其中的全部元素
(6)在使用ObjectOutputStream
ObjectInputStream流时、会先讲LinkedList的capacity读写入到流中、然后将元素一一读取/写入。
3.3源码分析之HashMap
HashMap初始化大小是 16 ,扩容因子默认0.75(可以指定初始化大小,和扩容因子) 扩容机制.(当前大小 和 当前容量 的比例超过了 扩容因子,就会扩容,扩容后大小为 一倍。例如:初始大小为 16 ,扩容因子 0.75 ,当容量为12的时候,比例已经是0.75 。触发扩容,扩容后的大小为 32.)
put方法:put过程是先计算hash然后通过hash与table.length取摸计算index值,然后将key放到table[index]位置,当table[index]已存在其它元素时,会在table[index]位置形成一个链表,将新添加的元素放在table[index],原来的元素通过Entry的next进行链接,这样以链表形式解决hash冲突问题,当元素数量达到临界值(capactiy*factor)时,则进行扩容,是table数组长度变为table.length*2
3.4源码分析之ConcurrentHashMap
ConcurrentHashMap的锁分段技术:
首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
ConcurrentHashMap的get:
Segment的get操作实现非常简单和高效。先经过一次再哈希,然后使用这个哈希值通过哈希运算定位到segment,再通过哈希算法定位到元素,代码如下:
ConcurrentHashMap的读是否要加锁,为什么?
ConcurrentHashMap完全允许多个读操作并发进行,读操作并不需要加锁。
ConcurrentHashMap总结:
(1)用分离锁实现多个线程间的更深层次的共享访问。
(2)用HashEntery对象的不变性来降低执行读操作的线程在遍历链表期间对加锁的需求。
(3)通过对同一个Volatile变量的写 / 读访问,协调不同线程间读 / 写操作的内存可见性。使用分离锁,减小了请求同一个锁的频率。
参考博客:
https://blog.csdn.net/login_sonata/article/details/76598675
3.5源码分析之HashTable
(1)Hashtable也是一个散列表,它存储的内容是键值对(key-value)映射,Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。
(2)默认构造函数,指定的容量大小是11;加载因子是0.75
3.6源码分析之Vector
(1)Vector是内部是以动态数组的形式来存储数据的。
(2)Vector具有数组所具有的特性、通过索引支持随机访问、所以通过随机访问Vector中的元素效率非常高、但是执行插入、删除时效率比较地下、具体原因后面有分析。
(3)Vector实现了AbstractList抽象类、List接口、所以其更具有了AbstractList和List的功能、前面我们知道AbstractList内部已经实现了获取Iterator和ListIterator的方法、所以Vector只需关心对数组操作的方法的实现、
(4)Vector实现了RandomAccess接口、此接口只有声明、没有方法体、表示Vector支持随机访问。
(5)Vector实现了Cloneable接口、此接口只有声明、没有方法体、表示Vector支持克隆。
(6)Vector实现了Serializable接口、此接口只有声明、没有方法体、表示Vector支持序列化、即可以将Vector以流的形式通过ObjectOutputStream来写入到流中。
(7)Vector是线程安全的
3.7源码分析之HashSet
(1)HashSet由哈希表(实际上是一个HashMap实例)支持,不保证set的迭代顺序,并允许使用null元素。
(2)基于HashMap实现,API也是对HashMap的行为进行了封装,可参考HashMap
3.8源码分析之LinkedHashMap
(1)LinkedHashMap继承于HashMap,底层使用哈希表和双向链表来保存所有元素,并且它是非同步,允许使用null值和null键。
(2)基本操作与父类HashMap相似,通过重写HashMap相关方法,重新定义了数组中保存的元素Entry,来实现自己的链接列表特性。该Entry除了保存当前对象的引用外,还保存了其上一个元素before和下一个元素after的引用,从而构成了双向链接列表。
3.9源码分析之LinkedHashSet
对于LinkedHashSet而言,它继承与HashSet、又基于LinkedHashMap来实现的。LinkedHashSet底层使用LinkedHashMap来保存所有元素,它继承与HashSet,其所有的方法操作上又与HashSet相同。
3.10详细文档
https://blog.csdn.net/qq_25868207/article/details/55259978