JAVA 集合整理
JAVA集合总结
List:
1.概括
* 1. List 是一个接口,它继承于Collection的接口。它代表着有序的队列。
* 2. AbstractList 是一个抽象类,它继承于AbstractCollection。AbstractList实现List接口中除size()、get(int location)之外的函数。
* 3. AbstractSequentialList 是一个抽象类,它继承于AbstractList。AbstractSequentialList 实现了“链表中,根据index索引值操作链表的全部函数”
* 4. ArrayList, LinkedList, Vector, Stack是List的4个实现类。
* ArrayList 是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。
* LinkedList 是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随机插入、随机删除效率低。
* Vector 是矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的(加了锁)
* Stack 是栈,它继承于Vector。它的特性是:先进后出(FILO, First In Last Out) 栈也是线程安全的。
2.使用场景
* 1. 快速插入,删除元素,使用LinkedList
* 2. 快速随机访问元素,使用ArrayList
* 3. 多线程操作环境下为了保证数据安全应该使用Vector
3.详细介绍
* ArrayList:
* ArrayList基于数组实现,是一个动态的数组队列,他的 初始容量=10 他的容量可以自动增长 新的容量=1.5*原始容量
```java
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 每次扩容 原始容量+原始容量/2
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:
// 将数据copy 到新的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
```
* ArrayList继承了AbstractList,实现了RandomAccess、Cloneable和Serializable接口
```java
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
```
* 实现了RandomAccess接口,提供了随机访问功能,实际上就是通过下标序号进行快速访问。
* 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
* 实现了Serializable接口,支持序列化.
* ArrayList非线程安全
* LinkedList:
* LinkedList 是基于链表实现的,是一个双向链表,可以用作堆栈,队列,双端队列,线程不安全,继承AbstractSequentialList实现List、Deque、Cloneable、Serializable。
```java
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
```
*LinkedList继承AbstractSequentialList,AbstractSequentialList 实现了get(int index)、set(int index, E element)、addint index, Eelement) 和remove(int index)这些函数。这些接口都是随机访问List的 随机访问效率很低
* LinkedList 实现 List 接口,能对它进行队列操作。
* LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
* LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
* LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输
* Vector:
* Vector和ArrayList差不多都是基于数组实现的
* Vector和ArrayList的方法基本一样 但是 Vector 在方法中加了synchronized 修饰 所以Vector是线程安全的,当我们用多线程时考虑数据安全,应该使用 Vector
* Vector默认大小是 10 当Vector 扩容时 若容量增加系数 大于0,则将容量的值增加“容量增加系数”;否则,将容量大小增加一倍。
```java
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);
}
```
Map:
1.概括:
* Map是“键值对”映射的抽象接口。
* AbstractMap实现了Map中的绝大部分函数接口。它减少了“Map的实现类”的重复编码。
* SortedMap有序的“键值对”映射接口。
* NavigableMap是继承于SortedMap的,支持导航函数的接口。
* HashMap, Hashtable, TreeMap, WeakHashMap这4个类是“键值对”映射的实现类。它们各有区别
* HashMap 是基于“拉链法”实现的散列表。一般用于单线程程序中。
* Hashtable 也是基于“拉链法”实现的散列表。它一般用于多线程程序中。
* WeakHashMap也是基于“拉链法”实现的散列表,它一般也用于单线程程序中。相比HashMap,WeakHashMap中的键是“弱键”,当“弱键”被GC回收时,它对应的键值
对也会被从WeakHashMap中删除;而HashMap中的键是强键。
* TreeMap 是有序的散列表,它是通过红黑树实现的。它一般用于单线程中存储有序的映射。
2.使用场景
* HashMap、WeakHashMap、TreeMap 非线程安全
* Hashtable 在方法中加了synchronized修饰 线程安全
* HashMap Hashtable 是无序的
* TreeMap 是有序的
3.详细介绍
HashMap
1.HashMap通过拉链法:通过table数组存储,数组的每一个元素都是一个Entry;而一个Entry就是一个单向链表,Entry链表中的每一个节点就保存了 key-value键值对数据。 2. 添加key-value键值对首先,根据key值计算出哈希值,再计算出数组索引(即,该key-value在table中的索引)。然后,根据数组索引找到Entry(即,单向链表 )再遍历单向链表,将key和链表中的每一个节点的key进行对比。若key已经存在Entry链表中,则用该value值取代旧的value值;若key不存在Entry链表中 ,则新建一个key-value节点,并将该节点插入Entry链表的表头位置。 删除key-value键值对:删除键值对,相比于“添加键值对”来说,简单很多。首先,还是根据key计算出哈希值,再计算出数组索引(即,该key-value在table中的索引)。然后,根据索引找出Entry(即,单向链表)。若节点key-value存在与链表Entry中,则删除链表中的节点即可。 3. HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。 ``` public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { ... } ``` 4. 实现了Map接口,支持key-value键值对操作。支持“添加key-value键值对”、“获取key”、“获取value”、“获取map大小”、“清空map”等基本的key-value键值对 操作 5. 实现了Cloneable接口,意味着能被克隆。 6. 实现了java.io.Serializable接口,意味着支持序列化,能通过序列化去传输。 7. 线程不安全 8. HashMap 的初始容量是 16 扩展的时候 每次扩展二倍 ``` void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length);//容量扩为原来容量的2倍。 hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); } ``` 9. HashMap 添加元素时 使用的自定义的哈希算法 ``` static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } // 将“key-value”添加到HashMap中 public V put(K key, V value) { // 若“key为null”,则将该键值对添加到table[0]中。 if (key == null) return putForNullKey(value); // 若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。 int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; // 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出! if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } // 若“该key”对应的键值对不存在,则将“key-value”添加到table中 modCount++; addEntry(hash, key, value, i); return null; } ``` 10. HashMap key-value 存储数据是允许key-value 为空 key为null 时会添加到 table 的第一个位置 11. HashMap不支持contains(Object value)方法,没有重写toString()方法。
- Hashtable
1. Hashtable 基本和HashMap 差不多
2. Hashtable 的默认大小为11 每次扩充 新容量=旧容量*2+1
@SuppressWarnings("unchecked")
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
//每次扩充 *2 +1
int newCapacity = (oldCapacity << 1) + 1;
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
3. Hashtable 线程安全
4. Hashtable 没有自定义hash 算法
```JAVA
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode(); //直接采用的hashcode
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
5. Hashtable支持contains(Object value)方法,而且重写了toString()方法
- TreeMap
* TreeMap 是一个有序的 key-value 基于红黑树的 NavigableMap 实现
* TreeMap 可以被克隆 实现了Cloneable 接口
* TreeMap 支持序列化
* TreeMap containKey get put remove 的时间复杂度都是 log
* TreeMap 可以创建时根据提供的Comparator 进行排序
- WeakHashMap
1. WeakHashMap 继承于AbstractMap,实现了Map接口。
2. 和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。
不过WeakHashMap的键是“弱键”。在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。更精确地说,对于一个给定的键,其映射
的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。某个键被终止时,它对应的键值对也就从映射中有效地移除了。
3. 弱键回收的步骤:
通过WeakReference和ReferenceQueue实现的
(1)新建WeakHashMap,将“键值对”添加到WeakHashMap中。实际上,WeakHashMap是通过数组table保存Entry键值对;每一个Entry实际上是一个单向链表,
即Entry是键值对链表。
(2) 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。
(3) 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们就是删除ta
ble中被GC回收的键值对。
4. 默认容量和加载因子和HashMap一样
5. 线程不安全
Set
1.概括
1.Set 是继承于Collection的接口。它是一个不允许有重复元素的集合。
2.AbstractSet 是一个抽象类,它继承于AbstractCollection,AbstractCollection实现了Set中的绝大部分函数,为Set的实现类提供了便利。
3.HastSet 和 TreeSet 是Set的两个实现类。
- HashSet依赖于HashMap,它实际上是通过HashMap实现的。HashSet中的元素是无序的。
- TreeSet依赖于TreeMap,它实际上是通过TreeMap实现的。TreeSet中的元素是有序的。
2.使用场景
hashSet 无序
TreeSet 有序
3.详细介绍
HashSet
HashSet 是一个没有重复元素的集合
它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素。
HashSet是非同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:Set s = Collections.synchronizedSet(new HashSet(...));
> TreeSet
TreeSet 是一个有序的集合,它的作用是提供有序的Set集合。它继承于AbstractSet抽象类,实现了NavigableSet<E>, Cloneable, java.io.Serializable接口。
TreeSet 继承于AbstractSet,所以它是一个Set集合,具有Set的属性和方法。
TreeSet 实现了NavigableSet接口,意味着它支持一系列的导航方法。比如查找与指定目标最匹配项。
TreeSet 实现了Cloneable接口,意味着它能被克隆。
TreeSet 实现了java.io.Serializable接口,意味着它支持序列化。
TreeSet是基于TreeMap实现的。TreeSet中的元素支持2种排序方式:自然排序 或者 根据创建TreeSet 时提供的 Comparator 进行排序。这取决于使用的构造方法。
TreeSet为基本操作(add、remove 和 contains)提供受保证的 log(n) 时间开销。
另外,TreeSet是非同步的。 它的iterator 方法返回的迭代器是fail-fast的。