JAVA集合总结

介绍

java的集合平时在工作中我们用得很多,有list,set,map。那么什么情况用哪个,以及他们都是怎么实现的,我想给自己总结一下,毕竟花了一天把源码看了一遍,过几天忘了就不划算了。

首先,我们把集合分成两类,存一个对象的叫Collection,存两个对象的叫Map。如下图所示:

JAVA集合总结

 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。

不允许添加相同的元素,

因为维护了一个双向链表,所以元素是有序的。

上一篇:HashSet与TreeSet 区别


下一篇:leetcode--找出数组中只出现一次的数字(位运算、set、常规解法)