介绍
java的集合平时在工作中我们用得很多,有list,set,map。那么什么情况用哪个,以及他们都是怎么实现的,我想给自己总结一下,毕竟花了一天把源码看了一遍,过几天忘了就不划算了。
首先,我们把集合分成两类,存一个对象的叫Collection,存两个对象的叫Map。如下图所示:
1 Map
首先说一下Map,实现Map接口的类主要有HashMap,LinkedHashMap,TreeMap,Hashtable,Properties。
Map与Collection并列存在。
Map中的key和value可以是任何引用类型的数据。
Map中的key不允许重复。
Map中的key和value都可以为空,但是key只能有一个为空。
1.1 HashMap
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
}
transient Node<K,V>[] table;
transient int size;
}
HashMap是集合中最重要的一个类。因为LinkedHashMap继承了它,HashSet底层用的是它,LinkedHashSet底层用的是它的子类。
HashMap是Map中使用频率最高的实现类。
在HashMap中,键不能重复,值可以重复
。键只能有一个空值,值可以有多个空值。如果添加相同的key会覆盖原来的value。
与HashSet一样,在JDK1.7中底层维护的是哈希表,在JDK1.8中底层维护的是数组、链表、红黑树,因此他们是不保证顺序的。
HashMap没有实现同步,因此线程不安全。
扩容机制:HashMap有三种情况下会扩容。第一、如果构造器没有设置容量,添加第一个元素的时候,会设置数组的容量为16。第二、阈值为cap*0.75,当现有元素数量大于阈值的时候,容量和阈值都会扩容为2倍。三、当链表满8个元素时,会判断数组的长度有没有达到64,如果没有达到64再次扩容,达到了64,就把这个链表转成红黑树。
1.2 LinkedHashMap
LinkedHashMap是HashMap的子类,它在HashMap的基础上维护了一个before,一个after,用来指明前后元素,因此这个集合中的元素存取顺序是一致的,我们通常说这个集合是有序的。
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
{
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
}
1.3 Hashtable
Hashtable的使用方法和HashMap相同,说两点不同的。在JDK1.7,他们两底层都是哈希表。到了JDK1.8,HashMap底层升级成了数组加链表加红黑树,而Hashtable没变。第二点,HashMap是线程不安全的,Hashtable是线程安全的。
扩容机制:其他集合在新建的时候都不会分配内存,只有这个集合,在无参构造函数中给数组分配了11个大小的空间,负载因子是0.75,阈值是8,让元素达到8,按照2*cap+1的公式扩容,也就是第一次扩容后是23。
1.4 Properties
这个类继承自Hashtable,用键值对的形式来保存数据,多用于从xxx.properties文件中加载数据到Properties类对象中,并进行读取和修改。
1.5 TreeMap
这个类底层维护的是红黑树,因此它可以用来排序。
2 Collection
接口有两个实现接口List和Set。
在List接口的实现类中,元素允许重复,但是在Set接口的实现类中元素不允许重复。
2.1 List
List中有三个实现类比较常用,ArrayList、LinkedList、Vector。他们中的元素都是有序的(存取顺序一致)。提供使用索引的方式获取元素。
2.1.1 ArrayList
它底层维护的是一个数组,这个数组用完了之后就会扩容。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
transient Object[] elementData;
private int size;
}
扩容机制:当创建ArrayList时,如果使用的是无参构造函数,则初始容量为0,第一次添加元素时扩容为10,之后扩容为1.5.如果使用指定大小的构造器,则容量为指定大小,之后同样按照1.5倍扩容。
2.1.2 LinkedList
它底层维护的时一个双向链表。可以添加重复元素,允许元素为空。
线程不安全,没有实现同步。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
}
2.1.3 Vector
它底层也是一个数组。
Vector类操作方法带有Synchronized是线程同步的,即线程安全的。
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
protected Object[] elementData;
protected int elementCount;
protected int capacityIncrement;
}
同样底层都是数组,vector的扩容机制和ArrayList不同。
扩容机制:如果是无参构造函数,默认容量是10,之后按照2倍扩容。支持有参构造函数之后按照2倍扩容。
2.2 Set
Set接口的实现类添加和取出顺序是不一致的,并且不提供索引。
2.2.1 HashSet
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
private transient HashMap<E,Object> map;
}
HashSet底层是HashMap。
可以存放空值,但是只能有一个。
HashSet不保证元素有序。
HashSet中不能有重复的元素。
扩容机制:通HashMap。
2.2.2 LinkedHashSet
LinkedHashSet是HashSet的子类。
底层是LinkedHashMap。
不允许添加相同的元素,
因为维护了一个双向链表,所以元素是有序的。