Java中集合类的关系图谱
Collection关系图:
Map关系图:
Collection vs. Collections
首先要明确的是,Collection 和 Collections是两个不同的概念。Collection是一个接口,所有的集合类(除Map外)都要继承(实现)自该接口。它提供了对集合对象进行基本操作的通用接口方法。Collections是一个包装类,它包含有各种有关集合操作的静态多态方法。(Collections是一个工具类,不能实例化)
参考:
Java集合类(四)—TreeSet
Java中集合类的关系图谱
一、 ArrayList与LinkedList源码分析
1、ArrayList
内部结构
/**
* Default initial capacity.
* ArrayList 默认的数组容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
* 用于空实例的共享空数组实例
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
* 另一个共享空数组实例,用的不多,用于区别上面的EMPTY_ELEMENTDATA
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
* ArrayList底层的容器
*/
// Android-note: Also accessed from java.util.Collections
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
* 当前存放了多少个元素 并非数组大小
*/
private int size;
ArrayList是基于动态数组的数据结构。实现了list接口,是以数组的方式实现的。所谓动态数组是这样实现的,如果没有指定数组的大小,则申请默认大小为10 的数组,当元素个数增加,数据无法存储时,系统会另外申请一个长度为当前长度的1.5倍的数组,然后把之前的数据拷贝(浅复制)到新建的数组中。
数组的特征是可以使用索引的方式来快速定位对象的位置。适合读取数据。
2、LinkedList
LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。有关索引的操作可能从链表头开始遍历到链表尾部,也可能从尾部遍历到链表头部,这取决于看索引更靠近哪一端。
LinkedList 实现 List 接口,能对它进行队列操作。
LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
LinkedList 是非同步的。
内部结构:
transient int size = 0;
/**
* Pointer to first node. 指向链表头部
*/
transient Node<E> first;
/**
* Pointer to last node.指向链表尾部
*/
transient Node<E> last;
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
具体请参考:
二、Map
1、HashMap & ConcurrentHashMap
- HashMap:实现了Map接口,数组+链表+红黑树,依据hashCode存储数据。非线程安全。
- ConcurrentHashMap:线程安全。实现了ConcurrentMap接口,使用锁分段技术,将数据分成一段一段的存储,给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。【具体细节待整理!!!】
- 如果不涉及到多线程处理的情况,就用hashMap,因为效率比较高。在有并发请求的场景中,如果数据的强一致性比较重要,那么就请使用hashTable,因为**ConcurrentHashMap的get,clear,iterator 都是弱一致性的**。如果效率要求比较高,那么就使用ConcurrentHashMap,因为他不会像hashTable那样产生阻塞。【数据一致性问题有待学习!!!】
具体参考:
HashMap实现原理及源码分析
Hash线程不安全
Java中集合类的关系图谱
2、HashTable
HashTable的类结构:
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
// Entry数组:用于存放集合中的元素
private transient Entry<K,V>[] table;
// 集合中Entry键值对的数量,并不是HashTable容器的大小
private transient int count;
// 阈值
private int threshold;
// 装载因子/加载因子
private float loadFactor;
// 记录集合的修改次数:用于实现"fail-fast"快速失败机制
private transient int modCount = 0;
}
HashTable 继承了 Dictionary 类,实现了 Map 接口。其中 Dictionary 类是可将任何键映射到相应值的类(如 Hashtable)的抽象父类。每个键和每个值都是一个对象。在任何一个 Dictionary 对象中,每个键至多与一个值相关联。Map是”key-value键值对”接口。
HashTable与HashMap区别:
- HashTable 基于 Dictionary 类,而 HashMap 是基于AbstractMap。
- Hashtable中key和value都不允许为null,而HashMap中key和value都允许为null(key只能有一个为null,而value则可以有多个为null)。
- hashTable线程安全,HashMap非线程安全。HashTable的同步方法都被synchronized修饰,当一个线程访问同步方法时,其他访问线程可能会进入阻塞或轮询状态。效率低。
- HashTable在不指定容量的情况下的默认容量为11,而HashMap为16。Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
- Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
具体参考:
二、 HashSet与TreeSet
1、 HashSet
HashSet是以HashMap作为底层数据结构实现的。HashMap 是作为键值对 key-value 进行存储的,而 HashSet不是键值对,那么选择 HashMap 作为实现,其原理就是存储在 HashSet 中的数据 作为 Map 的 key,而 Map 的value 统一为 一个常量字段PRESENT。对HashSet调用add、remove、contains等操作,其实都是在内部通过调用HashMap的相应操作来实现的。
具体参考:
JDK1.8源码(八)——java.util.HashSet 类