一:直观框架图简单版:详细版:Java集合框架Java集合框架主要包括两种类型的容器,一种是集合(Collection),另一种是图(Map)。
Collection接口又有3种子类型,List、Set和Queue,再下面是一些抽象类,最后是具体实现类,常用的有ArrayList、lixxxxnkedList、HashSet、lixxxxnkedHashSet、HashMap、lixxxxnkedHashMap等等。
Map常用的有HashMaplixxxxnkedHashMap等。
二、Collection接口1.ListList接口扩展自Collection,它可以定义一个允许重复的有序集合,从List接口中的方法来看,List接口主要是增加了面向位置的操作,允许在指定位置上操作元素,同时增加了一个能够双向遍历线性表的新列表迭代器ListIterator。
AbstractList类提供了List接口的部分实现,AbstractSequentialList扩展自AbstractList,主要是提供对链表的支持。
下面介绍List接口的两个重要的具体实现类,也是我们可能最常用的类,ArrayList和lixxxxnkedList。
1.1ArrayList通过阅读ArrayList的源码,我们可以很清楚地看到里面的逻辑,它是用数组存储元素的,这个数组可以动态创建,如果元素个数超过了数组的容量,那么就创建一个更大的新数组,并将当前数组中的所有元素都复制到新数组中。
假设第一次是集合没有任何元素,下面以插入一个元素为例看看源码的实现。
1.2.1、找到add()实现方法。
3.4.5.6.public boolean add(E e) {7.8.ensureCapacityInternal(size + 1);// Increments modCount!!9.10.elementData[size++] = e;11.12.return true;13.14.}15.16.17.18.2、此方法主要是确定将要创建的数组大小。
19.20.21.22.private void ensureCapacityInternal(intminCapacity) {23.24.if (elementData ==DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {25.26.minCapacity = Math.max(DEFAULT_CAPACITY minCapacity);27.28.}29.30.31.32.ensureExplicitCapacity(minCapacity);33.34.}35.36.37.38.private void ensureExplicitCapacity(intminCapacity) {39.40.modCount++;41.42.if (minCapacity - elementData.length >0)43.44.grow(minCapacity);45.46.}47.48.49.50.3、最后是创建数组,可以明显的看到先是确定了添加元素后的大小之后将元素复制到新数组中。
51.52.53.54.private void grow(int minCapacity) {55.56.// overflow-conscious code57.58.int oldCapacity = elementData.length;59.60.int newCapacity = oldCapacity +(oldCapacity >> 1);61.62.if (newCapacity - minCapacity < 0)63.64.newCapacity = minCapacity;65.66.if (newCapacity - MAX_ARRAY_SIZE >0)67.68.newCapacity = hugeCapacity(minCapacity);69.70.// minCapacity is usually close to size sothis is a win:71.72.elementData = Arrays.copyOf(elementDatanewCapacity);73.74.}1.2lixxxxnkedList同样,我们打开lixxxxnkedList的源文件,不难看到lixxxxnkedList是在一个链表中存储元素。
在学习数据结构的时候,我们知道链表和数组的最大区别在于它们对元素的存储方式的不同导致它们在对数据进行不同操作时的效率不同,同样,ArrayList与lixxxxnkedList也是如此,实际使用中我们需要根据特定的需求选用合适的类,如果除了在末尾外不能在其他位置插入或者删除元素,那么ArrayList效率更高,如果需要经常插入或者删除元素,就选择lixxxxnkedList。
1.3CopyOnWriteArrayListCopyOnWriteArrayList,是一个线程安全的List接口的实现,它使用了ReentrantLock锁来保证在并发情况下提供高性能的并发读取。
2.SetSet接口扩展自Collection,它与List的不同之处在于,规定Set的实例不包含重复的元素。
在一个规则集内,一定不存在两个相等的元素。
AbstractSet是一个实现Set接口的抽象类,Set接口有三个具体实现类,分别是散列集HashSet、链式散列集lixxxxnkedHashSet和树形集TreeSet。
2.1HashSet散列集HashSet是一个用于实现Set接口的具体类,可以使用它的无参构造方法来创建空的散列集,也可以由一个现有的集合创建散列集。
在散列集中,有两个名词需要关注,初始容量和客座率。
客座率是确定在增加规则集之前,该规则集的饱满程度,当元素个数超过了容量与客座率的乘积时,容量就会自动翻倍。
从输出结果我们可以看到,规则集里最后有4个元素,而且在输出时元素还是无序的。
2.2lixxxxnkedHashSetlixxxxnkedHashSet是用一个链表实现来扩展HashSet类,它支持对规则集内的元素排序。
HashSet中的元素是没有被排序的,而lixxxxnkedHashSet中的元素可以按照它们插入规则集的顺序提取。
2.3TreeSetTreeSet扩展自AbstractSet,并实现了NavigableSet,AbstractSet扩展自AbstractCollection,树形集是一个有序的Set,其底层是一颗树,这样就能从Set里面提取一个有序序列了。
在实例化TreeSet时,我们可以给TreeSet指定一个比较器Comparator来指定树形集中的元素顺序。
树形集中提供了很多便捷的方法。
3.Queue队列是一种先进先出的数据结构,元素在队列末尾添加,在队列头部删除。
Queue接口扩展自Collection,并提供插入、提取、检验等操作。
上图中,方法offer表示向队列添加一个元素,poll()与remove()方法都是移除队列头部的元素,两者的区别在于如果队列为空,那么poll()返回的是null,而remove()会抛出一个异常。
方法element()与peek()主要是获取头部元素,不删除。
接口Deque,是一个扩展自Queue的双端队列,它支持在两端插入和删除元素,因为lixxxxnkedList类实现了Deque接口,所以通常我们可以使用lixxxxnkedList来创建一个队列。
PriorityQueue类实现了一个优先队列,优先队列中元素被赋予优先级,拥有高优先级的先被删除。
三、Map接口1.HashMapHashMap是基于哈希表的Map接口的非同步实现,继承自AbstractMap,AbstractMap是部分实现Map接口的抽象类。
在平时的开发中,HashMap的使用还是比较多的。
我们知道ArrayList主要是用数组来存储元素的,lixxxxnkedList是用链表来存储的,那么HashMap的实现原理是什么呢?先看下面这张图:在之前的版本中,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。
但是当链表中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。
而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
下面主要通过源码介绍一下它的实现原理。
HashMap存储元素的数组transient Node<KV>[] table;数组的元素类型是Node<KV>,Node<KV>继承自Map.Entry<KV>,表示键值对映射。
1.2.static class Node<KV> implementsMap.Entry<KV> {3.4.final int hash;5.6.final K key;7.8.V value;9.10.Node<KV> next;11.12.13.14.//构造函数 ( Hash值键值下一个节点 )15.16.Node( int hash K key V value Node<KV> next) {17.18.this.hash = hash;19.20.this.key = key;21.22.this.value = value;23.24.this.next = next;25.26.}27.28.29.30.public final K getKey() { return key; }31.32.public final V getValue(){ return value; }33.34.public final String toString(){ return key + "=" + value; }35.36.37.38.public final int hashCode() {39.40.return obxxxxjects.hashCode(key) ^ obxxxxjects.hashCode(value);41.42.}43.44.45.46.public final V setValue(V newValue) {47.48.V oldValue = value;49.50.value = newValue;51.52.return oldValue;53.54.}55.56.57.58.public final boolean equals(obxxxxject o) {59.60.if (o == this)61.62.return true;63.64.if (o instanceof Map.Entry) { 65.66.Map.Entry<??> e = (Map.Entry<??>)o;67.68.if (obxxxxjects.equals(keye.getKey()) &&69.70.obxxxxjects.equals(valuee.getValue()))71.72.return true;73.74.}75.76.return false;77.78.}79.80.}接下来我们看下HashMap的put操作。