Java 9.0 各个集合的基本原理总结

基本特性

跳转目录

这里写图片描述

List

实现类父类数据结构是否线程安全扩容处理是否接受null值
LinkListAbstractSequentialList双向链表接受多个null值
ArrayListAbstractList数组1.5倍扩容接受多个null值
VectorAbstractList数组自定义或者2倍扩容接受多个null值
StackVector数组2倍扩容接受多个null值

Map

Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复

实现类父类数据结构是否线程安全扩容处理是否接受null值其他
HashtableDictionary哈希表(数组)2n+1key和value都不能为null值遍历时获取的数据是随机的
WeakHashMapAbstractMap哈希表 (数组)2倍扩容key和value可为null值遍历时获取的数据是随机的
HashMapAbstractMap哈希表(单链表+红黑树)2倍扩容key和value可为null值遍历时获取的数据是随机的
LinkedHashMapHashMap哈希表(双向链表)2倍扩容key和value可为null值遍历时获取的数据和插入时是一样的
TreeMapAbstractMap红黑树key不能为null,value可以为null遍历时获取的数据是递增排序好的

Set

用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复.
Set内部使用的是Map实现,Map的key不能重复,Set利用这一原理实现Set的元素值不重复.

实现类父类数据结构是否线程安全扩容处理是否接受null值其他
TreeSetAbstractSet(TreeMap实现)红黑树2n+1元素不能为null值遍历时获取的数据是排序好的
HashSetAbstractSet(HashMap实现)哈希表 (单链表+红黑树)2倍扩容元素可为null值遍历时获取的数据是随机的
LinkHashSetHashSet(内部是LinkedHashMap实现)哈希表 (单链表+红黑树)2倍扩容元素可为null值遍历时获取的数据和插入时是一样的

1. List

跳转目录

LinkList是一个双向链表结构的集合.

1.1.1 继承体系

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

1.1.2 私有变量

//元素数量
 transient int size = 0;
//头节点
transient Node<E> first;
//尾节点
transient Node<E> last;

1.1.3 数据结构

双向链表
下面是链表的节点定义.

private static class Node<E> {
   //元素值
    E item;
    //指向前驱节点
    Node<E> next;
    //指向后继节点
    Node<E> prev;
}

1.1.4 是否线程安全

非线程安全

1.1.4 迭代器

ListIterator

1.1.5 是否接受null值

接受多个null值

1.2 ArrayList

跳转目录

1.2.1 继承体系

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

1.2.2私有变量

//初始容量
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//保存数据的数组
transient Object[] elementData; 
//list的数据量
private int size;
//用于迭代时检测当前数组是否有更新
protected transient int modCount = 0;

1.2.3数据结构

ArrayList使用数组保存数据.

1.2.4扩容处理

当容量超过当前数据的长度时,ArrayList便会进行扩容.

获取新的容量,进行的是1.5倍扩容.

 private int newCapacity(int minCapacity) {
   // overflow-conscious code
    int oldCapacity = elementData.length;
    //1.5倍扩容
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity <= 0) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return minCapacity;
    }
    return (newCapacity - MAX_ARRAY_SIZE <= 0)
        ? newCapacity
        : hugeCapacity(minCapacity);
}

扩容处理,也就是实现数组复制

private Object[] grow(int minCapacity) {
    return elementData = Arrays.copyOf(elementData,
                                       newCapacity(minCapacity));
}

1.2.5迭代器访问

注意迭代过程中其他线程不能修改ArrayList

ArrayList<Integer> list = new ArrayList<Integer>();
list.add(234);
list.add(121);

//foreach
for(Integer dat:list){

    System.out.println(dat);
}

System.out.println();
//Iterator
Iterator it = list.iterator();
while(it.hasNext())
{
    System.out.println(it.next());
}
System.out.println();
//ListIterator
ListIterator listIterator = list.listIterator();
while(listIterator.hasNext())
{
    listIterator.add(123);
    System.out.println(listIterator.next());
}

1.2.6 是否线程安全

非线程安全

1.2.7 是否接受null值

可接受多个null值.

1.3 Vetor

跳转目录

1.3.1 继承体系

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

1.3.2 数据结构

使用数组保存数据

1.3.3 私有属性

//保存数据的数组
protected Object[] elementData;
//当前元素的数量
protected int elementCount;
//自定义每次扩容时增加的容量
protected int capacityIncrement;
//serialVersionUID
private static final long serialVersionUID = -2767605614048989439L;

1.3.4 扩容处理

每次添加元素时如果容量不够便会进行扩容处理.
获取新容量:
1.如果已经定义了每次扩容所增加的容量,那么扩容后的总容量就是当前的容量与自定义增加容量之和.
2.如果没定义,那就是两倍扩容.

 private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //获取新容量
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    if (newCapacity - minCapacity <= 0) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return minCapacity;
    }
    return (newCapacity - MAX_ARRAY_SIZE <= 0)
        ? newCapacity
        : hugeCapacity(minCapacity);
}

扩容处理,就是以新的容量复制旧的数组.

private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }

1.3.4 迭代器访问

public  static void main(String[] args) {

  //list
  Vector<Integer> vector = new Vector<Integer>();
  vector.add(234);
  vector.add(121);
  vector.add(null);
  vector.add(null);
  //foreach
  for(Integer dat:vector){
      System.out.println(dat);
  }
  System.out.println();
  //Iterator
  Iterator it = vector.iterator();
  while(it.hasNext())
  {
      System.out.println(it.next());
  }
  System.out.println();
 //ListIterator
  ListIterator listIterator = vector.listIterator();
  while(listIterator.hasNext())
  {
      listIterator.add(123);
      System.out.println(listIterator.next());
  }
}

1.3.5 是否线程安全

Vector使用synchronized同步了部分方法,因此是线程安全的.

1.3.6 是否接受null值

可以接受多个null值.

1.4 Stack

跳转目录
Stack是Vector的子类,是一个栈,因此特性和上述的Vector一致.
相关方法

//构造器
 public Stack() {   };
 //向栈顶插入元素
 public E push(E item){};
 //弹出栈顶的元素(删除元素)
 public synchronized E pop(){} ;
 //获取栈顶的元素(不删除元素)
 public synchronized E peek()
 //检测栈是否为空
 public boolean empty() 
 //获取元素的索引
 public synchronized int search(Object o);

2 HashMap

2.1 Hashtable

跳转目录

2.1.1 继承体系

跳转目录

extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable 

2.1.2 私有变量

//保存各个节点的哈希表
private transient Entry<?,?>[] table;
//元素节点的数量
private transient int count;
//rehashed阀值,当元素数量大于threshold = (int)(capacity * loadFactor)进行rehashed
private int threshold;
//加载因子
private float loadFactor;
//记录当前对集合修改的次数,实现fail-fast机制
private transient int modCount = 0;
//序列化id
private static final long serialVersionUID = 1421746759512286392L;

2.1.3 保存结构

Entry是一个单链表结构

private transient Entry<?,?>[] table;
 private static class Entry<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Entry<K,V> next;
 }

2.1.4 实现原理

使用哈希表进行保存.
计算每个key的哈希值,通过哈希值计算保存位置索引值index.再存入table中,如果出现哈希冲突,则使用链地址法解决.多个元素节点Entry在同一个index处形成单链表.

2.1.5 index值计算

int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;

2.1.6rehash处理

可以看出,使用两倍扩容

protected void rehash() {
    int oldCapacity = table.length;
    Entry<?,?>[] oldMap = table;

    // overflow-conscious code
    //新容量是旧容量的两倍
    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;
        }
    }
}

2.1.7是否线程安全

是.在方法上使用synchronized实现同步.

2.1.8是否接受null值

key和value都不能为null值

2.2 WeakHashMap

跳转目录

2.2.1 继承体系

public class WeakHashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>

2.2.2 私有属性

//默认初始容量
 private static final int DEFAULT_INITIAL_CAPACITY = 16;
 //最大容量
 private static final int MAXIMUM_CAPACITY = 1 << 30;
 //负载因子
 private static final float DEFAULT_LOAD_FACTOR = 0.75f;
 //存放数据的数组,类型为 Entry<K,V>[]
 Entry<K,V>[] table;
 //元素数量
 private int size;
 //扩容阀值,threshold = capacity * load factor
 private int threshold;
 //负载因子
 private final float loadFactor;
 //保存被gc回收的队列
 private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
 //修改次数计数器,用于检测迭代过程中是否被修改
 int modCount;

2.2.3 节点类型

可以看出节点是一个单链表的结构.
每次创建节点都会把节点的key进行弱引用.

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
        V value;
        final int hash;
        Entry<K,V> next;

        Entry(Object key, V value,
              ReferenceQueue<Object> queue,
              int hash, Entry<K,V> next) {
            super(key, queue);
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }
}

2.3.4 数据结构

哈希表,使用链地址法解决哈希冲突.

2.3.5实现原理

WeakHashMap的key是“弱键”,即是WeakReference类型的。ReferenceQueue是一个队列,它会保存被GC回收的“弱键”。实现步骤是:

1.新建WeakHashMap,将“键值对”添加到WeakHashMap中。实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表.

2.当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中.

3.当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对.

例子说明

public class WeakHashMapDemo {

    public  static  void main(String[] args){

        Object obj = new Object();

        WeakHashMap  weakHashMap = new WeakHashMap();
        weakHashMap.put(obj,123);
        //将obj去除强引用
        obj =  null;
        //通知gc进行垃圾回收
        System.gc();

        Iterator it = weakHashMap.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry en = (Map.Entry) it.next();
            System.out.println("weakMap:" + en.getKey() + ": " + en.getValue());
        }
    }
}

1.当存在obj = null;时,weakHashMap中的obj对象没有了强引用,当进行gc回收后,weakHashMap中的obj对象即被回收,所以不会打印任何东西.
2.当注释掉obj = null;时,weakHashMap中的obj对象仍然存在强引用,当进行gc回收后,weakHashMap中的obj对象不会被回收,所以会打印以下内容.

weakMap:java.lang.Object@4c75cab9: 123

2.3.6 哈希值和索引值计算

//消除key为null时的影响
private static Object maskNull(Object key) {
    return (key == null) ? NULL_KEY : key;
}
//计算哈希值
final int hash(Object k) {
   int h = k.hashCode();
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
//通过哈希值计算索引值
private static int indexFor(int h, int length) {
     return h & (length-1);
 }

2.3.7 同步quere和table表的数据

当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对.

private void expungeStaleEntries() {
    for (Object x; (x = queue.poll()) != null; ) {
        synchronized (queue) {
            @SuppressWarnings("unchecked")
                Entry<K,V> e = (Entry<K,V>) x;
            int i = indexFor(e.hash, table.length);

            Entry<K,V> prev = table[i];
            Entry<K,V> p = prev;
            while (p != null) {
                Entry<K,V> next = p.next;
                if (p == e) {
                    if (prev == e)
                        table[i] = next;
                    else
                        prev.next = next;
                    // Must not null out e.next;
                    // stale entries may be in use by a HashIterator
                    e.value = null; // Help GC
                    size--;
                    break;
                }
                prev = p;
                p = next;
            }
        }
    }
}

2.3.8 扩容处理

进行的是两倍扩容

//put方法调用
 resize(tab.length * 2);

2.3.9 是否接受null值

WeakHashMap对key为null的情况进行了处理,WeakHashMap可以接受key和value为null.但是只能存在一个key为null.

//消除key为null时的影响
private static Object maskNull(Object key) {
    return (key == null) ? NULL_KEY : key;
}

2.3.10 是否是线程安全的

非线程安全.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值