Java容器
Java容器
Java中容器类可以分为两大类:Collection与Map。
- Collection:存储对象的集合;
- Map:存储键值对。
Collection
Set
TreeSet
基于红黑树,支持有序性操作。
HashSet
基于哈希表实现,支持快速查找,不支持有序性操作。
LinkedHashSet
基于双向链表实现,只能顺序访问,可以快速在其中间插入和删除元素。
List
ArrayList
基于动态数组实现,支持随机访问。
ArrayList源码
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
//基于数组实现,RandomAccess表示支持随机访问。
private static final int DEFAULT_CAPACITY = 10;
//默认大小为10
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
//int newCapacity = oldCapacity + (oldCapacity >> 1); 表示每次扩容为旧容量的1.5倍。
transient Object[] elementData;
//使用transient修饰,默认数组不会被序列化。
Vector
和ArrayList相似,是线程安全的(除了私有方法,其他方法都使用了synchronized修饰)。
public synchronized void copyInto(Object[] anArray) {
System.arraycopy(elementData, 0, anArray, 0, elementCount);
}
public synchronized void trimToSize() {
modCount++;
int oldCapacity = elementData.length;
if (elementCount < oldCapacity) {
elementData = Arrays.copyOf(elementData, elementCount);
}
}
public synchronized int capacity() {
return elementData.length;
}
public synchronized boolean isEmpty() {
return elementCount == 0;
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
//扩容的时候看传入的参数,如果大于0就扩大为原来基础上加上你传入的参数,如果小于0就扩大为原来的两倍(实际开发中因该很少调用构造函数,所以大多数情况是扩大为原来的两倍)。
``
LinkedList
基于双向链表实现,只能顺序访问,但是可以快速在链表中间插入和删除元素。
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;
}
}
//使用双向链表且每个节点存储了上下两个节点。
transient Node<E> first;
transient Node<E> last;
//每个链表存储了first和last指针
Queue
LinkedList
常用于实现双向队列
PriorityQueue
基于堆结构实现,可以实现优先队列。
Map
TreeMap
基于红黑树实现
HashMap
基于哈希表实现
在jdk1.7及以前的时候使用的是数组和链表,在jdk1.8及以后使用的是数组、链表和红黑树。
transient Entry[] table;
//不会被序列化
拉链法
HashMap<String, String> map = new HashMap<>();
map.put("K1", "V1");
map.put("K2", "V2");
map.put("K3", "V3");
- 新建一个 HashMap,默认大小为 16;
- 插入 <K1,V1> 键值对,先计算 K1 的 hashCode 为 115,使用除留余数法得到所在的桶下标 115%16=3。
- 插入 <K2,V2> 键值对,先计算 K2 的 hashCode 为 118,使用除留余数法得到所在的桶下标 118%16=6。
- 插入 <K3,V3> 键值对,先计算 K3 的 hashCode 为 118,使用除留余数法得到所在的桶下标 118%16=6,插在 <K2,V2> 前面。
注意:链表的插入是头插法的方式进行的。
查找:
- 计算键值对所在的桶;
- 在链表上顺序查找,时间复杂度显然和链表的长度成正比。
扩容的时候扩大为原来的2倍。
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
HashTable
与HashMap类似,是线程安全的,但不推荐使用(推荐使用ConcurrentHashMap)
LinkedHashMap
使用双向链表来维护元素的顺序,顺序插入顺序或者最近最少使用(LRU)顺序。
继承HashMap
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>