首页 > 编程语言 >Java中List、Map常见实现类

Java中List、Map常见实现类

时间:2023-03-15 11:15:39浏览次数:36  
标签:Map Java int elementData List newCapacity value minCapacity final

一、List

1.ArrayList

底层是数组实现,线程不安全

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final int DEFAULT_CAPACITY = 10;
    transient Object[] elementData; 
    private int size;
    ...
}

因为底层是数组,所以读取速度快,增改查效率低,但可以自动扩容,其扩容原理的

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
 }
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
 }
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
 private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

扩容机制是每次扩容到原先的1.5倍

int newCapacity = oldCapacity + (oldCapacity >> 1);

 

 

2. LinkedList

LinkedList底层是双向链表,与ArrayList相同,都是线程不安全的,但因为是链表实现,与ArrayList相反,增删改的速度很快,查询的速度慢

因为是双向链表,不存在扩容机制(新增节点)

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;
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
    ...
}

 

3.Vector

与ArrayList相似,但是因为加了synchronized关键字,线程安全,也因此查询效率没有ArrayList高

    public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        return elementData(index);
    }

另外,与ArrayList每次1.5倍增长不同,vector的扩容机制是以每次2倍速度扩容

    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
    private void ensureCapacityHelper(int minCapacity) {
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    private void grow(int minCapacity) {
        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);
    }

 

 

总结:

  ArrayList  LinkedList Vector
底层实现 数组 双向链表 数组
线程安全 不安全 不安全 安全(synchronized)
扩容机制 每次1.5倍 新增node节点 每次2倍

 

 

二、Map

1.HashMap

特点

  • 允许null键和null值
  • key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写:equals()和hashCode()
  • value构成的集合是Collection:无序的、可以重复的。所以,value所在的类要重写:equals()
  • key-value构成一个entry,entry是无序的、不可重复的。
  • 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCode 值也相等。
  • 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。
  • 线程不安全

JDK1.7和1.8不同特点

  • 1.7以前是数组+链表,使用头插法
  • 1.8以后是数组+链表/红黑树,使用尾插法

扩容机制

当HashMap中链表的对象个数如果达到了8个,如果最大容量没有达到64,那么HashMap会扩容一倍。

如果已经达到了64,那么这个链会变成树,结点类型由Node变成TreeNode类型。当然,如果当映射关系被移除后,下次resize方法时判断树的结点个数低于6个,也会把树再转为链表。

 

2.HashTable

特点和HashMap一致,但有一个不同是HashTable是线程安全的。

 

3.ConcurrentHashMap

ConcurrentHashMap与HashMap的区别是比如value等数据,会使用volatile 修饰,保证了获取时的可见性。

static final class HashEntry<K,V> { 
       final K key;                       // 声明 key 为 final 型
       final int hash;                   // 声明 hash 值为 final 型 
       volatile V value;                 // 声明 value 为 volatile 型
       final HashEntry<K,V> next;      // 声明 next 为 final 型 

       HashEntry(K key, int hash, HashEntry<K,V> next, V value) { 
           this.key = key; 
           this.hash = hash; 
           this.next = next; 
           this.value = value; 
       } 
}

 

ConcurrentHashMap 通过使用锁分段技术来保证线程安全。它将哈希表分成一些段(Segment),每个段都是一个独立的哈希表。在 ConcurrentHashMap 中,读操作和写操作可以同时进行,因为每个段都有自己的锁,不同的段之间可以并行处理。这种方式相当于把一个大的哈希表拆分成了多个小的哈希表,不同的线程可以同时操作不同的哈希表段,从而提高了并发性。

当一个线程要进行写操作时,它只需要获取对应的段的锁,而不是整个哈希表的锁。这样做的好处是,如果不同的线程要修改不同的哈希表段,它们就不会相互影响,从而提高了并发性。在读操作时,ConcurrentHashMap 并不需要加锁,因为每个段都是独立的,读操作不会影响其他段的数据。

static final class Segment<K,V> extends ReentrantLock implements Serializable { 

       transient volatile int count; 

       transient int modCount; 

       transient int threshold; 

       transient volatile HashEntry<K,V>[] table; 

       final float loadFactor; 

       Segment(int initialCapacity, float lf) { 
           loadFactor = lf; 
           setTable(HashEntry.<K,V>newArray(initialCapacity)); 
       } 
}

 

标签:Map,Java,int,elementData,List,newCapacity,value,minCapacity,final
From: https://www.cnblogs.com/wintermist/p/17217778.html

相关文章