# Java 集合框架详情

Java 集合框架详情

集合框架总览图

在这里插入图片描述

Iterable接口

Iterable接口是Java集合框架的顶级接口,实现此接口可以通过迭代器遍历自身元素。Java容器中,Collection继承Iterable接口。

Iterable接口源码

public interface Iterable<T> {
	// 创建一个类型为T的Iterator的实例
    Iterator<T> iterator();

    // 遍历操作集合内的元素
    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接口

Iterator接口中提供了对hasNext()、next()等元素的操作方式。Iterator的实现类中完成具体的元素的操作。Collection的子类中匿名内部类会实现Iterator接口进行对容器中元素的操作。


Collection接口

Collection接口中定义了元素属性、元素方法等方法。

@Test
public void test1(){

    Collection list = new ArrayList();
    Collection linkList = new LinkedList();
    Collection set = new HashSet();
    Collection linkSet = new LinkedHashSet();
    Collection queue = new ArrayDeque();
    Collection deQueue = new DelayQueue();
}

继承Collection的接口

List

List接口扩展Collection接口中的方法。


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

在这里插入图片描述

  • ArrayList概述:ArrayList是基于数组实现的,能够动态扩容。ArrayList不是线程安全的,实现了
    Serializable接口能够序列化传输。支持快速随机访问,实现了Cloneable接口可以实现对象克隆。
  • ArrayList初始化
// 默认无参构造函数
public ArrayList() ;
// 制定大小初始化
public ArrayList(int initialCapacity);
// Collection实例初始化
public ArrayList(Collection<? extends E> c)
  • ArrayList动态扩容:在add()中
// 最具体的扩容方法
private void grow(int minCapacity) {
    // 目前数组的长度
    int oldCapacity = elementData.length;
    // 新的数组的大小 旧数组大小+就数组大小右移1位
    // newCapacity =oldCapacity + 0.5 * oldCapacity  
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // MAX_ARRAY_SIZE 为int最大值减去8
    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);
}

如果通过无参构造的话,初始数组容量为0,当真正对数组进行添加时,才真正分配容量。每次按照 oldCapacity + (oldCapacity >> 1)。通过copeOf的方式扩容。


Set

Set接口继承Collection扩展Collection接口中的方法,

HashSet类关系

在这里插入图片描述


Java.util.Map接口

HashMap类关系
public class HashMap<K,V> extends AbstractMap<K,V>    implements Map<K,V>, Cloneable, Serializable {}

在这里插入图片描述

HashMap底层存储结构

HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做一个Entry。HashMap中key和value都可以是null。

  • 数组:数组中的元素以Entry<K,V>的方式存储。
/**
  * Basic hash bin node, used for most entries.  (See below for
  * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
  */
static class Node<K,V> implements Map.Entry<K,V> {
    // 定位数组索引位置
    final int hash;
    final K key;
    V value;
    // 链表的下一个节点
    Node<K,V> next; 

    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
    public final K getKey()        { return key; }
    public final V getValue()      { return value; }
    public final String toString() { return key + "=" + value; }
    public final int hashCode() {}
    public final V setValue(V newValue) {}
    public final boolean equals(Object o) {}
}
  • 链表
  • 红黑树:链表过长时候转换为红黑树,而当链表长度太长(默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能,其中会用到红黑树的插入、删除、查找等算法。
HashMap 使用哈希表存储
  • Map.put(“name”,“张三”)

调用hashCode()计算name的哈希值,通过hash算法的高位运算和取模运算定位该键值对存储的位置。当key的hashCode值相同时候则发生hash碰撞。好的Hash算法和扩容机制可以减小hash碰撞的概率。

int threshold;             // 所能容纳的key-value对极限 
final float loadFactor;    // 负载因子
int modCount;  // 记录HashMap内部结构发生变化的次数
int size; // Map中存的键值对数量
  • Node[K,V] table[]的初始化长度是16,负载因子(默认值是0.75),threshold是HashMap所能容纳的最大数据量的Node(键值对)个数。threshold = length * Load factor。也就是说,在数组定义好长度之后,负载因子越大,所能容纳的键值对个数越多。
  • threshold就是在此Load factor和length(数组长度)对应下允许的最大元素数目,超过这个数目就重新resize(扩容),扩容后的HashMap容量是之前容量的两倍。
HashMap确定数组索引位置
  • hashCode算索引
  • 高位运算
  • 取模运算:h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。
static final int hash(Object key) {
    int h;
    // 计算key的hash值 h = key.hashCode()
    // 高位参与运算 h^(h>>>16) h异或(h右移16位)
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
扩容机制

在这里插入图片描述

  • 扩容(resize)就是重新计算容量,向HashMap对象里不停的添加元素,而HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。当然Java里的数组是无法自动扩容的,方法是使用一个新的数组代替已有的容量小的数组,就像我们用一个小桶装水,如果想装更多的水,就得换大水桶。
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;
        }
         // 没超过最大值,就扩充为原来的2倍
        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 {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
     // 计算新的resize上限
    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循环把oldTab中的每个节点node,reHash操作并移动到新的数组newTab中
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                // 若是单个节点,即没有后继next节点,则直接 newTab 在进行重定位
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                //若节点为TreeNode,则需要进行红黑树的rehash操作
                else if (e instanceof TreeNode)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                //else则节点为链表,需进行链表的rehash操作,链表重组并保持原有顺序
                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;
                        //通过与位运算&,判断rehash后节点位置是否发生改变
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                //loHead 指向新的 hash 在原位置的头节点
                                loHead = e;
                            else
                                //loTail 指向新的 hash 在原位置的尾节点
                                loTail.next = e;
                            loTail = e;
                        }
                        //else则rehash后节点位置变为:原位置+oldCap位置
                        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;
}

Map 基本操作和常用方法


找出Map中Values的最大和最小值(Arrays.sort(object))
@Test
public void test1(){
    Map<String, String> map=new HashMap<>();
    map.put("4","5");
    map.put("5","8");
    map.put("6","9");
    map.put("7","7");
    Collection<String> values = map.values();
    Object[] objects = values.toArray();
    Arrays.sort(objects);
    String min = String.valueOf(objects[0]);
    String max = objects[objects.length - 1].toString();
    System.out.println("最大和最小值为:"+max+"===="+min);
}

map.get() 或者map.getOrDefault()
  • map.get(“test”):当值为空时候返回null
  • map.getOrDefault(“test”):当值为空时候可以给给定默认值
@Test
public void test6() {
    Map<String, String> map = new HashMap<>();
    map.put("6", "9");
    map.put("7", "7");
    String test = map.get("test");
    String test1 = map.getOrDefault("test", "test1");
    System.out.println(test);
    System.out.println(test1);
}

判断集合为空 map.isEmpty()
@Test
public void test6() {
    Map<String, String> map = new HashMap<>();
    Map<String, String> map1=new HashMap<>();
    boolean empty = map.isEmpty(); # false
    boolean empty1 = map1.isEmpty(); #true
}

Map遍历
map.foreach()
@Test
public void test2(){
    Map<String, String> map=new HashMap<>();
    map.put("6","9");
    map.put("7","7");
    map.forEach((x,y)->{
        System.out.println("键值为:"+x+"===="+y);
    });
}
Map的Entry
@Test
public void test3(){
    Map<String, String> map=new HashMap<>();
    map.put("6","9");
    map.put("7","7");
    for(Map.Entry entry:map.entrySet()){
        System.out.println(entry.getKey()+"====="+entry.getValue());
    }
}
For循环map.keyset()
@Test
public void test4(){
    Map<String, String> map=new HashMap<>();
    map.put("6","9");
    map.put("7","7");
    for(String entry:map.keySet()){
        System.out.println(entry+"===="+map.get(entry));
    }
}

得到Map中的所有Values map.values()
@Test
public void test5(){
    Map<String, String> map=new HashMap<>();
    map.put("6","9");
    map.put("7","7");
    Collection<String> values = map.values();
    System.out.println(values);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值