0
点赞
收藏
分享

微信扫一扫

Java中的集合底层数据结构分析

一 集合

Java中的集合分为两大类Collection接口,与Map接口。
image.png
image.png

1.1 Iterable与Iterator

  • Iterable接口是单列集合的最高级。
public interface Iterable<T> {
   
    // 返回一个迭代器对象
    Iterator<T> iterator();

  	// 对Iterable的每个元素执行给定的操作,直到处理完所有元素或该操作引发异常。
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    
	// 并行遍历迭代器
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}
  • Iterator迭代器对象
public interface Iterator<E> {
   
    // 是否有下一个元素
    boolean hasNext();

   // 下一个元素
    E next();

  	// 从基础集合中移除此迭代器返回的最后一个元素(可选操作)
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

  // 对每个剩余元素执行给定的操作,直到所有元素都已处理
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}

1.2 Collection接口

  • 在add方法加上断点调试
/**
 * @Author shu
 * @Date: 2022/02/25/ 9:22
 * @Description
 **/
public class ArrayListTest {
    public static void main(String[] args) {
        ArrayList<Integer> list =new ArrayList<>();
        for (int i = 0; i < 15; i++) {
            list.add(i);
        }
    }
}
public interface Collection<E> extends Iterable<E> {
   	// 返回集合大小
    int size();
	// 是否为空
    boolean isEmpty();
 	// 是否包含这个元素
    boolean contains(Object o);
	// 迭代器
    Iterator<E> iterator();
	// 转换成数组
    Object[] toArray();
	// 增加一个元素
    boolean add(E e);
	// 移除一个元素
    boolean remove(Object o);
	// 包含一个数组对象
    boolean containsAll(Collection<?> c);
	// 增加一个数组对象
    boolean addAll(Collection<? extends E> c);
	// 移除一个数组对象
    boolean removeAll(Collection<?> c);
	// 并行遍历
    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, 0);
    }
	// 流计算
    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
	// 并行流
    default Stream<E> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }
}

1.2.1 ArrayList

image.png

  • 重要参数
	// 初始化容器容量
    private static final int DEFAULT_CAPACITY = 10;

   	// 空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};

  	// 用于默认大小的空实例的共享空数组实例。
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

 	// 缓存数组,用于扩容
    transient Object[] elementData; // non-private to simplify nested class access

   	// 大小
    private int size;
  • 构造器
// 指定了初始化容量
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

// 无参构造器
 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

// 指定集合
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
  • 扩容过程,ArrayList在调用无参构造方法时创建的是一个长度为0的空数组,当调用add()方法添加元素时,ArrayList才会触发扩容机制
public boolean add(E e) {
    	// 检查是否扩容	
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }


private void ensureCapacityInternal(int minCapacity) {
    	// 是否为空数组
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 最大值,默认10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
		// 确定是否真正的扩容
        ensureExplicitCapacity(minCapacity);
    }


private void ensureExplicitCapacity(int minCapacity) {
    	// 当前集合修改的次数
        modCount++;

        // 说明容量不够了
        if (minCapacity - elementData.length > 0)
            // 进行扩容操作
            grow(minCapacity);
    }

/**
先将旧容量右移1位,再加上旧容量就得到了新容量,正数右移1位相当于除以2,在该基础上加旧容量,则等价于新容量 = 旧容量 * 1.5,所以才有ArrayList每次扩容为旧容量的1.5倍的说法,最后调用Arrays.copyOf()方法进行拷贝,并将elementData指向新数组,而旧数组因为没有引用指向它,很快就会被垃圾收集器回收掉。
**/
 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
     	// 位移运算,向右移动以为除以2
     	// 默认为10 ,新数组容量为10+5=15
        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:
     	// 复制成新数组,保留原来的元素 
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

1.2.2 Vector

  • 在add方法加上断点调试
package Collection;
import java.util.Vector;


/**
 * @Author shu
 * @Date: 2022/02/25/ 9:30
 * @Description
 **/

public class VectorTest {
    public static void main(String[] args) {
        Vector<Integer> list =new Vector<>();
        for (int i = 0; i < 15; i++) {
            list.add(i);
        }
    }
}

  • 重要参数
	// 缓存数组,用于扩容
    protected Object[] elementData;

	// 计算缓存数组大小
    protected int elementCount;

 	// 缓存数组大小
    protected int capacityIncrement;
  • 构造器
 // 构造一个具有指定初始容量和容量增量的空向量。
public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

 // 指定了初始化容量
public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }


   
// 无参构造,默认初始化容量为10
public Vector() {
        this(10);
    }
  • 扩容过程
// synchronized关键字修饰,线程安全
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) {
        // 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);
    }

image.png

1.2.3 LinkedList

package Collection;

import java.util.LinkedList;

/**
 * @Author shu
 * @Date: 2022/02/25/ 9:44
 * @Description
 **/
public class LinkListTest {
    public static void main(String[] args) {
        LinkedList<Integer> list =new LinkedList<>();
        for (int i = 0; i < 15; i++) {
            list.add(i);
        }
    }
}
  • 重要属性

image.png

 private static class Node<E> {
     	// 节点内容
        E item;
     	// 节点上一个指向
        Node<E> next;
     	// 节点上一个指向
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }


 	transient int size = 0;

  	// 指向第一个节点的指针。
    transient Node<E> first;

	// 指向最后一个节点的指针。
    transient Node<E> last;
  • 基本方法
	// 获取第一个元素
    public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }

   	// 获取最后一个元素
    public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }

	// 移除第一个元素
    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }

   // 移除最后一个元素
    public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l);
    }

  	// 增加第一个元素
    public void addFirst(E e) {
        linkFirst(e);
    }

   	// 增加最后一个元素
    public void addLast(E e) {
        linkLast(e);
    }

	// 增加一个元素
    public boolean add(E e) {
        linkLast(e);
        return true;
    }

  • 基本执行流程
  public boolean add(E e) {
        linkLast(e);
        return true;
    }


	/**
     * Links e as last element.第一次的话,first,next都指向一个node
     */
    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++;
    }


 	/**
     * Unlinks non-null first node f.
     */
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        //获取元素信息
        final E element = f.item;
        // 获取下一个节点元素
        final Node<E> next = f.next;
        // 赋值为空
        f.item = null;
        f.next = null; // help GC
        // 上一个元素指向一个元素
        first = next;
        if (next == null)
            last = null;
        else
            next.prev = null;
        size--;
        modCount++;
        return element;
    }

1.3 Set接口

image.png
image.png

public interface Set<E> extends Collection<E> {
   	// 大小
    int size();
	// 是否为空
    boolean isEmpty();
	// 包含元素
    boolean contains(Object o);
	// 迭代器
    Iterator<E> iterator();
	// 转换成数组
    Object[] toArray();
	// 添加元素
    boolean add(E e);
	// 移除元素
    boolean remove(Object o);

    boolean containsAll(Collection<?> c);

   
    boolean addAll(Collection<? extends E> c);

   
    boolean retainAll(Collection<?> c);

    boolean removeAll(Collection<?> c);

    void clear();

    boolean equals(Object o);

   
    int hashCode();

   
    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, Spliterator.DISTINCT);
    }
}

1.3.1 HashSet

image.png

package Set;
import java.util.HashSet;

/**
 * @Author shu
 * @Date: 2022/02/25/ 10:47
 * @Description
 **/
public class HashSetTest {
    public static void main(String[] args) {
        HashSet<Object> hashSet = new HashSet<>();
        hashSet.add("hhhh");
        hashSet.add("bbb");
    }
}
  • 构造器, 从构造器中可以看出其底层实际是HashMap
 public HashSet() {
        map = new HashMap<>();
    }

 public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }

 public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
  • 基本流程,add方法进行断点调式,添加第一个元素
// 添加方法
public boolean add(E e) {
    	// PRENTMap 中的对象关联的虚拟值
        return map.put(e, PRESENT)==null;
    }


public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

// 进行Hash计算
static final int hash(Object key) {
        int h;
    	// 采用位运算,避免指针碰撞
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
 }

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        // 判断数组是否为空,长度是否为0,是则进行扩容数组初始化
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        // 通过hash算法找到数组下标得到数组元素,为空则新建
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
             // 找到数组元素,hash相等同时key相等,则直接覆盖
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            // 该数组元素在链表长度>8后形成红黑树结构的对象
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                // 该数组元素hash相等,key不等,同时链表长度<8.进行遍历寻找元素,有就覆盖无则新建
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        // 新建链表中数据元素
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            // 链表长度>=8 结构转为 红黑树
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }



/**
初始化或加倍表大小。如果为空,则按照字段阈值中保存的初始容量目标进行分配。否则,因为我们使用二次幂展开,每个 bin 中的元素必须保持相同的索引,或者在新表中以二次幂的偏移量移动
**/
final Node<K,V>[] resize() {
    
        Node<K,V>[] oldTab = table;
    
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
    	// 临界值=(容量)*(负载因子)
        int oldThr = threshold;
    
        int newCap, newThr = 0;
    
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
    
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
    	// 第一次初始化
        else { 
            // table默认16
            newCap = DEFAULT_INITIAL_CAPACITY;
            // 临界值:临界值=(容量)*(负载因子)=16*0.75=12
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
    	//临界值
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    	// 表结构初始化完毕,并返回
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }




  • 添加重复元素
 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
     
        Node<K,V>[] tab;
     	Node<K,V> p; 
     	int n, i;
     
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
     
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
     
 		// 重复元素
        else {
            Node<K,V> e;
            K k;
            // (1)准备加入的key 和p指向的Node 结点的key 是同一个对象
            // (2) p指向的Node 结点的key 的equals()和准备加入的key比较后相同
            if (p.hash == hash &&
                ((k = p.key) == key 
                 || (key != null && key.equals(k))))
                // 将节点
                e = p;
            
            // 是不是一颗红黑树
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p)
                .putTreeVal(this, tab, hash, key, value);
            
            else
            {
                // 加入链表后面
                // 依次和该链表的每一个元素比较后,都不相同,则加入到该链表的最后				
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            // 是否转成红黑树,table的size大于64
                            // if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
                            treeifyBin(tab, hash);
                        break;
                    }
                  // 依次和该链表的每六个元素比较过程中,如果有相同情况,就break
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            
            // 返回旧数据
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
     
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
     
        return null;
  
    }

  • 首选判断table是否为空,数组长度为空,将会进行第一次初始化。(在实例化HashMap是,并不会进行初始化数组)。
  • 进行第一次resize()扩容之后。开始通过hash算法寻址找到数组下标。若数组元素为空,则创建新的数组元素。若数组元素不为空,同时hash相等,key不相等,同时不是TreeNode数据对象,将遍历该数组元素下的链表元素。若找到对应的元素,则覆盖,如果没有找到,就新建元素,放入上一个链表元素的next中,在放入元素之后,如果条件满足"链表元素长度>8",则将该链表结构转为"红黑树结构"。
  • 找到对应的数组元素或者链表元素,同时创建新的数据元素或者覆盖元素之后。如果条件满足元素大小size>允许的最大元素数量threshold,则再一次进行扩容操作。每次扩容操作,新的数组大小将是原始的数组长度的两倍。
  • put操作完成。

1.3.2 LinkedHashSet

image.png

package Set;

import java.util.LinkedHashSet;

/**
 * @Author shu
 * @Date: 2022/02/28/ 9:41
 * @Description
 **/
public class LinkedHashSetTest {
    public static void main(String[] args) {
        LinkedHashSet<Integer> hashSet=new LinkedHashSet<>();
        hashSet.add(1);
        hashSet.add(2);
        hashSet.add(3);
    }
}

  • 构造器,我们可以看出LinkHashSet 其实是LinkedHashMap
 public LinkedHashSet(int initialCapacity) {
        super(initialCapacity, .75f, true);
    } 
// 初始化容量16 ,负载因子0.75f
    public LinkedHashSet() {
        super(16, .75f, true);
    }

   HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

  • 数据节点类型 Entry

image.png
image.png
image.png


    /**
     * HashMap.Node subclass for normal LinkedHashMap entries.
     */
    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);
        }
    }
  • 添加元素

image.png
image.png

// 调用添加方法
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
// 调用Map添加方法
 public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
// 计算Hadh值
static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

真正添加元素
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
     
        Node<K,V>[] tab;
     	Node<K,V> p; 
     	int n, i;
     
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
     
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
     
 		// 重复元素
        else {
            Node<K,V> e;
            K k;
            // (1)准备加入的key 和p指向的Node 结点的key 是同一个对象
            // (2) p指向的Node 结点的key 的equals()和准备加入的key比较后相同
            if (p.hash == hash &&
                ((k = p.key) == key 
                 || (key != null && key.equals(k))))
                // 将节点
                e = p;
            
            // 是不是一颗红黑树
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p)
                .putTreeVal(this, tab, hash, key, value);
            
            else
            {
                // 加入链表后面
                // 依次和该链表的每一个元素比较后,都不相同,则加入到该链表的最后				
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            // 是否转成红黑树,table的size大于64
                            // if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
                            treeifyBin(tab, hash);
                        break;
                    }
                  // 依次和该链表的每六个元素比较过程中,如果有相同情况,就break
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            
            // 返回旧数据
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
     
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
     
        return null;
  
    }

1.3.4 TreeSet

TreeMap:基于红黑二叉树的NavigableMap的实现,线程非安全,不允许null,key不可以重复,value允许重复,存入TreeMap的元素应当实现Comparable接口或者实现Comparator接口,会按照排序后的顺序迭代元素,两个相比较的key不得抛出classCastException。主要用于存入元素的时候对元素进行自动排序,迭代输出的时候就按排序顺序输出
image.png

  • 构造器
TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }

// 由无参构造器可以看出底层是TreeMap
public TreeSet() {
        this(new TreeMap<E,Object>());
    }

public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }

  • 树节点
 static final class Entry<K,V> implements Map.Entry<K,V> {
        K key;
        V value;
        Entry<K,V> left;
        Entry<K,V> right;
        Entry<K,V> parent;
        boolean color = BLACK;

        /**
         * Make a new cell with given key, value, and parent, and with
         * {@code null} child links, and BLACK color.
         */
        Entry(K key, V value, Entry<K,V> parent) {
            this.key = key;
            this.value = value;
            this.parent = parent;
        }
 }
  • 增加方法
public V put(K key, V value) {
      //定义一个数节点来保存根元素
        Entry<K,V> t = root;
        //如果t==null,表明是一个空链表
        if (t == null) {
        //如果根节点为null,将传入的键值对构造成根节点(根节点没有父节点,所以传入的父节点为null)
            root = new Entry<K,V>(key, value, null);
            //设置该集合的size为1
            size = 1;
            //修改此时+1
            modCount++;
            return null;
        }
        // 记录比较结果
        int cmp;
        Entry<K,V> parent;
        // 分割比较器和可比较接口的处理
        Comparator<? super K> cpr = comparator;
        // 有比较器的处理,即采用定制排序
        if (cpr != null) {
            // do while实现在root为根节点移动寻找传入键值对需要插入的位置
            do {
                //使用parent上次循环后的t所引用的Entry
                // 记录将要被掺入新的键值对将要节点(即新节点的父节点)
                parent = t;
                // 使用比较器比较父节点和插入键值对的key值的大小
                cmp = cpr.compare(key, t.key);
                // 插入的key较大
                if (cmp < 0)
                    t = t.left;
                // 插入的key较小
                else if (cmp > 0)
                    t = t.right;
                // key值相等,替换并返回t节点的value(put方法结束)
                else
                    return t.setValue(value);
            } while (t != null);
        }
        // 没有比较器的处理
        else {
            // key为null抛出NullPointerException异常
            if (key == null)
                throw new NullPointerException();
            Comparable<? super K> k = (Comparable<? super K>) key;
            // 与if中的do while类似,只是比较的方式不同
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        // 没有找到key相同的节点才会有下面的操作
        // 根据传入的键值对和找到的“父节点”创建新节点
        Entry<K,V> e = new Entry<K,V>(key, value, parent);
        // 根据最后一次的判断结果确认新节点是“父节点”的左孩子还是又孩子
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        // 对加入新节点的树进行调整
        fixAfterInsertion(e);
        // 记录size和modCount
        size++;
        modCount++;
        // 因为是插入新节点,所以返回的是null
        return null;
    }

1.4 Map接口

image.png

  • Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)
  • Map集合中的元素,key和value的数据类型可以相同,也可以不同
  • Map集合中的元素,key不允许重复,value可以重复
  • Map集合中的元素,key和value是一一对应的
  • Map为了方便程序员的遍历,还会创建 EntrySet集合,该集合存放的元素的类型 Entry,而一个Entry对象就有k,v EntrySet<Entry<K,V>>即: transient Set<Map.Entry<K,V>> entrySet

1.4.1 HashMap

image.png
image.png
image.png

package Map;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author shu
 * @Version 1.0
 * @Date: 2022/04/16/ 14:05
 * @Description debug测试
 **/
public class HashMapTest {
    public static void main(String[] args) {
        Map<String,Integer> map=new HashMap<String,Integer>();
        map.put("1",101);
        map.put("2",102);
    }
}

  • 构造器
  /**
     * The default initial capacity - MUST be a power of two.
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

/**
     * Constructs an empty <tt>HashMap</tt> with the specified initial
     * capacity and the default load factor (0.75).
     *
     * @param  initialCapacity the initial capacity.
     * @throws IllegalArgumentException if the initial capacity is negative.
     */
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    /**
     * Constructs an empty <tt>HashMap</tt> with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
  • 增加过程
// 调用put方法
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

// 
真正添加元素
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
     
        Node<K,V>[] tab;
     	Node<K,V> p; 
     	int n, i;
         // 默认初始容量16
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
         // 是否在tab表中存在该值
        if ((p = tab[i = (n - 1) & hash]) == null)
            // 不存在该值直接添加
            tab[i] = newNode(hash, key, value, null);
 		// 在tab表中存在该值
        else {
            Node<K,V> e;
            K k;
           
            // 如果加入该值的Hash值相等且key相等,就替换原来的的key对应的值
            if (p.hash == hash &&
                ((k = p.key) == key 
                 || (key != null && key.equals(k))))
                // 将节点
                e = p;
            
            // 是不是一颗红黑树
            else if (p instanceof TreeNode)
                // 进添加到树节点中
                e = ((TreeNode<K,V>)p)
                .putTreeVal(this, tab, hash, key, value);
            
            else
            {
                // 加入链表后面
                // 依次和该链表的每一个元素比较后,都不相同,则加入到该链表的最后	
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            // 是否转成红黑树,table的size大于64
                            // if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
                            treeifyBin(tab, hash);
                        break;
                    }
                  // 依次和该链表的每六个元素比较过程中,如果有相同情况,就break
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            
            // 返回旧数据
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
     
        ++modCount;
        // 看大小是否达到阈值 
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
     
        return null;
  
    }
  • 进行树化

    final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode<K,V> hd = null, tl = null;
            do {
                TreeNode<K,V> p = replacementTreeNode(e, null);
                if (tl == null)
                    hd = p;
                else {
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);
            if ((tab[index] = hd) != null)
                hd.treeify(tab);
        }
    }

1.4.2 HashTable

image.png

  • 构造器 ,初始化容量为11,阈值8

image.png

// 使用指定的初始容量和默认加载因子 (0.75) 构造一个新的空哈希表。
public Hashtable() {
        this(11, 0.75f);
    }

// 使用指定的初始容量和指定的负载因子构造一个新的空哈希表。
public Hashtable(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load: "+loadFactor);

        if (initialCapacity==0)
            initialCapacity = 1;
        this.loadFactor = loadFactor;
        table = new Entry<?,?>[initialCapacity];
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    }

// 构造一个与给定 Map 具有相同映射的新哈希表。哈希表的初始容量足以容纳给定 Map 中的映射和默认加载因子 (0.75)
 public Hashtable(Map<? extends K, ? extends V> t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
    }
  • 添加方法
// 指定key映射到此哈希表中的指定value 。键和值都不能为null 。
可以通过使用与原始键相同的键调用get方法来检索该值。
public synchronized V put(K key, V value) {
        // 先判断值不能为Null
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        // 确报key的Hash值唯一
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        // 判断是否重复值
        for(; entry != null ; entry = entry.next) {
            // 交换值
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }
        // 增加到HashTable$Entry中
        addEntry(hash, key, value, index);
        return null;
    }

// 添加元素
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();
            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++;
    }
    

//增加此哈希表的容量并在内部重新组织此哈希表,以便更有效地容纳和访问其条目。当哈希表中的键数超过此哈希表的容量和负载因子时,将自动调用此方法。
protected void rehash() {
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;
 
        // 新数组的容量=旧数组长度*2+1
        int newCapacity = (oldCapacity << 1) + 1;
        // 保证新数组的大小永远小于等于MAX_ARRAY_SIZE
        // MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                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;
            }
        }
    }

image.png

  • Hashtable扩容的数组长度是旧数组长度乘以2加1
  • Hashtable采用取模的方式来计算数组下标,同时数组的长度尽量为素数或者奇数,这样的目的是为了减少Hash碰撞,计算出来的数组下标更加分散,让元素均匀的分布在数组各个位置。
  • 与HashMap不同的是。在扩容时Hashtable采用头插法的方式迁移数据,使用头插法效率会更高。因为头插法减少了遍历链表的时间,效率更高。
  • 虽然rehash()方法没有被synchronized修饰,这并不意味着rehash()方法非线程安全,这是因为所有调用rehash()的方法均被synchronized修饰。
  • 所以即使HashMap因为线程安全问题在JDK1.8抛弃了头插法而采用高地位平移算法,Hashtable在JDK1.8仍采用头插法迁移元素,因为Hashtable为了线程安全而设计,不会存在HashMap类似的问题。
  • Hashtable处于一个比较尴尬的位置,以至于官方也推荐如果不考虑线程安全,建议使用HashMap,如果考虑到同步效率,建议使用ConcurrentHashMap。

1.4.3 TreeMap

** **TreeMap继承AbstractMap,实现NavigableMap、Cloneable、Serializable三个接口。其中AbstractMap表明TreeMap为一个Map即支持key-value的集合, NavigableMap(更多)则意味着它支持一系列的导航方法,具备针对给定搜索目标返回最接近匹配项的导航方法 。
image.png
image.png
原理同上TreeSet

举报

相关推荐

0 条评论