Java 容器

一、容器图谱

在这里插入图片描述

在这里插入图片描述

二、Iterator 和 Iterable

Iterator是一个接口,该接口通常会以内部类的形式实现,可以使用 Iterator 对象遍历Collection(集合):

public class Main {
   
    public static void main(String[] args) {
   
        List<String> list = Arrays.asList("H", "E", "L", "L", "O");
        Iterator<String> iter = list.iterator();
        while(iter.hasNext()) {
   
            System.out.println(iter.next());
        }
    }
}

Collection 接口继承了Iterable接口,所以可以使用 Iterable 接口中的iterator()方法获取 Iterator 对象。

考虑使用for each 循环遍历集合:

for(String str : list) {
    System.out.println(str); }

反编译字节码:

        29: invokestatic  #17                 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
        32: astore_1
        33: aload_1
        34: invokeinterface #23,  1           // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
        39: astore_2
        40: aload_2
        41: invokeinterface #29,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
        46: ifeq          69
        49: aload_2
        50: invokeinterface #35,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
        55: checkcast     #7                  // class java/lang/String
        58: astore_3
        59: getstatic     #39                 // Field java/lang/System.out:Ljava/io/PrintStream;
        62: aload_3
        63: invokevirtual #45                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        66: goto          40
        69: return

发现其实使用的就是 Iterator 遍历的集合~

Iterator 接口还有一个remove方法:

  • 如果迭代器不支持删除操作,则该方法会抛出 UnsupportedOperationException 异常。(默认实现
  • 如果迭代器支持删除操作,则该方法会删除上次调用 next 方法时返回的元素。如果调用 remove 方法之前没有调用 next 方法,或者连续调用了两次 remove 方法,将会抛出一个 IllegalStateException 异常。

三、Collection

1. List

List 是一个有序集合,可以存储重复的元素。

1.1 ArrayList

ArrayList 是支持动态扩容的数组,它的底层是一个 Object 类型的数组,所以可以存放任何类型的元素。

① 构造对象
private static final int DEFAULT_CAPACITY = 10; // 默认初始容量
private static final Object[] EMPTY_ELEMENTDATA = {
   }; // 共享的容量为0的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
   }; // 共享的标记为默认容量的空数组
transient Object[] elementData; // 数组缓冲区,存储数组元素

public ArrayList() {
   
    // 引用标记为默认容量的空数组
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

public ArrayList(int initialCapacity) {
   
    if (initialCapacity > 0) {
   
        // 使用指定的容量创建数组
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
   
        // 引用容量为0的空数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
   
        // 抛出异常
        throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
    }
}

解析:

  • 构造对象时如果没有指定容量,则使用默认的初始容量,但是这时并不会真正创建一个容量为 10 的数组,而是引用一个共享的标记为默认容量的空数组。注意此时实际的容量还是0,添加元素时需要进行扩容。
  • 构造对象时如果指定的容量大于 0,则创建指定容量的数组;如果等于 0,则引用一个共享的容量为 0 的空数组;如果小于 0,则抛出异常。
② 添加元素
protected transient int modCount = 0; // AbstractList 中的字段,表示列表修改的次数(fail-fast 迭代器)
private int size; // 数组列表包含的元素数量
transient Object[] elementData; // 数组缓冲区,存储数组元素

public boolean add(E e) {
   
    modCount++;
    add(e, elementData, size);
    return true;
}

private void add(E e, Object[] elementData, int s) {
   
    // 如果元素数量等于数组缓冲区的容量则扩容
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}

// 将元素插入到指定位置
public void add(int index, E element) {
   
    // 检查index是否合法
    rangeCheckForAdd(index);
    modCount++;
    final int s;
    Object[] elementData;
    if ((s = size) == (elementData = this.elementData).length)
        elementData = grow();
    // 移动元素
    System.arraycopy(elementData, index,
                     elementData, index + 1,
                     s - index);
    elementData[index] = element;
    size = s + 1;
}
③ 删除元素
private int size; // 数组列表包含的元素数量
transient Object[] elementData; // 数组缓冲区,存储数组元素

// 删除指定位置的元素,并返回该元素
public E remove(int index) {
   
    // 检查index是否合法
    Objects.checkIndex(index, size);
    final Object[] es = elementData;

    @SuppressWarnings("unchecked") E oldValue = (E) es[index];
    fastRemove(es, index);

    return oldValue;
}

private void fastRemove(Object[] es, int i) {
   
    modCount++;
    final int newSize;
    // 如果删除的不是尾部的元素,则进行移动
    if ((newSize = size - 1) > i)
        System.arraycopy(es, i + 1, es, i, newSize - i);
    // 重置尾部元素的值
    es[size = newSize] = null;
}
④ 自动扩容
private static final int DEFAULT_CAPACITY = 10; // 默认初始容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
   }; // 默认容量的空数组
private int size; // 数组列表包含的元素数量
transient Object[] elementData; // 数组缓冲区,存储数组元素

private Object[] grow() {
   
    // 至少扩容为size+1
    return grow(size + 1);
}

private Object[] grow(int minCapacity) {
   
    // 数组缓冲区的容量
    int oldCapacity = elementData.length;
    // 如果当前容量大于0或者数组不为空
    if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
   
        // 计算新数组的容量
        int newCapacity = ArraysSupport.newLength(oldCapacity,
                minCapacity - oldCapacity,
                oldCapacity >> 1);
        // 浅拷贝数组,并使其容量为newCapacity
        return elementData = Arrays.copyOf(elementData, newCapacity);
    } else {
   
        // 创建容量至少为10的数组
        return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
    }
}

public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
   
    // 如果元素数量大于原来容量的1.5倍,则将容量变为元素数量,否则变为原来的1.5倍
    int prefLength = oldLength + Math.max(minGrowth, prefGrowth);
    // 如果计算结果没有溢出,则返回结果
    if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
   
        return prefLength;
    } else {
   
        // 如果计算结果溢出,调用hugeLength方法
        return hugeLength(oldLength, minGrowth);
    }
}

// 保守的最大数组长度
public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;

private static int hugeLength(int oldLength, int minGrowth) {
   
    // 至少需要的容量
    int minLength = oldLength + minGrowth;
    if (minLength < 0) {
   
        // 如果结果溢出,则抛出异常
        throw new OutOfMemoryError(
            "Required array length " + oldLength + " + " + minGrowth + " is too large");
    } else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {
   
        // 如果没有超过保守的最大数组长度,则返回保守的最大数组长度
        return SOFT_MAX_ARRAY_LENGTH;
    } else {
   
        // 如果超过了保守的最大数组长度,但是没有溢出,则返回该值
        return minLength;
    }
}
⑤ 手动扩容
private static final int DEFAULT_CAPACITY = 10; // 默认初始容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
   }; // 共享的标记为默认容量的空数组
transient Object[] elementData; // 数组缓冲区,存储数组元素

public void ensureCapacity(int minCapacity) {
   
    // 要求指定容量大于当前容量,并且如果指定容量小于默认容量,则当前数组列表不能是用无参构造新创建的
    if (minCapacity > elementData.length
        && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
             && minCapacity <= DEFAULT_CAPACITY)) {
   
        modCount++;
        grow(minCapacity);
    }
}

解析:如果已知数组列表需要添加大量元素,可以提前手动扩容避免频繁的扩容和浅拷贝操作。

⑥ 缩减容量
private static final Object[] EMPTY_ELEMENTDATA = {
   }; // 共享的容量为0的空数组
private int size; // 数组列表包含的元素数量
transient Object[] elementData; // 数组缓冲区,存储数组元素

public void trimToSize() {
   
    modCount++;
    // 如果元素数量小于当前容量
    if (size < elementData.length) {
   
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA
          : Arrays.copyOf(elementData, size);
    }
}

解析:添加元素时数组会自动扩容,但是删除元素不会缩减容量,所以在需要时我们可以调用该方法手动进行缩容。

1.2 LinkedList

LinkedList 的底层是一个双向链表,它实现了 List 和 Deque 接口。

① 构造对象
transient int size = 0; // 链表的元素数量
transient Node<E> first; // 指向头结点的指针
transient Node<E> last; // 指向尾结点的指针

// 结点内部类
private static class Node<E> {
   
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值