【Java】常用集合类框架

Collection 类关系图

img

容器,就是可以容纳 Java 对象的容器

优点:

  • 降低编程难度
  • 提高程序性能
  • 提高 API 之间的互操作性
  • 降低学习难度
  • 降低设计和实现相关 API 的难度
  • 增加程序的可复用性

一、Collection

容器主要包括 Collection 和 Map 两种 ,Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表。

Set

TreeSet

基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。

HashSet

基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。

LinkedHashSet

具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。

List

ArrayList

基于动态数组实现,支持随机访问。

Vector

和 ArrayList 类似,但它是线程安全的。

LinkedList

基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。

Queue

LinkedList

可以用它来实现双向队列。

PriorityQueue

基于堆结构实现,可以用它来实现优先队列。

二、Map

TreeMap

基于红黑树实现。

HashMap

基于哈希表实现。

HashTable

和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。

LinkedHashMap

使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。

三、Collection——ArrayList

概述

  • 实现了list接口、顺序容器

  • 元素存放的数据和与放进去的顺序相同

  • 允许有 null

  • 底层通过数组实现(Object数组)、可以容纳任何类型

  • 有一定的 capacity,容量不足的时候会自动扩大底层数组的大小

  • 方法

    • size(), isEmpty(), get(), set()方法均能在常数时间内完成
    • add()方法的时间开销跟插入位置有关,addAll()方法的时间开销跟添加元素的个数成正比
    • 其余方法大都是线性时间
  • 为了实现效率 ArrayList 没有实现同步 synchronized

    • 如果需要多个线程进行访问,用户可以手动同步,也可以用 Vector 来代替

img

ArrayList的实现

底层数据结构
	/**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access
    //关键字表示这个成员变量不会被序列化
    //即在对象序列化时,它不会被写入到输出流中。这样做是为了节省空间和提高性能。
    /**1
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;
构造函数
/**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    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);
        }
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
        这个构造函数用于创建一个初始容量为10的空列表。
        它使用了一个预定义的空Object数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA。
     */
    public ArrayList() {
   
   
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
        这个构造函数用于创建一个包含指定集合元素的列表。
        它首先将集合转换为一个Object数组,然后检查数组的类型是否为Object[]。
        如果不是,它会创建一个新的Object[]数组并复制元素。
        如果集合为空,它将使用一个空的Object数组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;
        }
    }
自动扩容

每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度

如果超出,数组将会进行扩容,以满足添加数据的需求。

数组扩容通过一个公开的方法ensureCapacity(int minCapacity)来实现。

在实际添加大量元素前,我也可以使用ensureCapacity来手动增加ArrayList实例的容量,以减少递增式再分配的数量。

数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。

这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造 ArrayList 实例时,就指定其容量,以避免数组扩容的发生。或者根据实际需求,通过调用ensureCapacity 方法来手动增加 ArrayList 实例的容量。

img

  /**
     * Increases the capacity of this <tt>ArrayList</tt> instance, if
     * necessary, to ensure that it can hold at least the number of elements
     * specified by the minimum capacity argument.
     *
     * @param   minCapacity   the desired minimum capacity
     */

    public void ensureCapacity(int minCapacity) {
   
   
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
  /**
    这个方法首先检查当前数组是否为默认的空数组,如果不是,则将最小扩展容量设置为 0;
    如果是,则将其设置为默认容量。然后,如果指定的最小容量大于最小扩展容量
    就调用 ensureExplicitCapacity(int minCapacity) 方法来确保有足够的空间容纳新的元素。
     */
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
   
   
            ensureExplicitCapacity(minCapacity);
        }
    }

    private void ensureCapacityInternal(int minCapacity) {
   
   
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
   
   
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
   
   
        modCount++;
        // 表示ArrayList结构修改次数的计数器
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    /**
    根据当前数组的长度计算新的容量,新容量等于旧容量加上旧容量的一半。
    然后,它会检查新容量是否小于所需的最小容量,如果是,则将新容量设置为所需的最小容量。
    接下来,它会检查新容量是否超过了最大数组大小
    (MAX_ARRAY_SIZE 如果超过了,就调用 hugeCapacity(int minCapacity)方法来确定合适的新容量。
    最后,它会使用 Arrays.copyOf() 方法将旧数组的内容复制到新的更大的数组中。
     */
    private void grow(int minCapacity) {
   
   
        // overflow-conscious code
        int oldCapacity = elementData.length;
        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);
    }

    /**
        用于处理特殊情况,当所需的最小容量超过最大数组大小时,它会抛出一个OutOfMemoryError异常,
        或者返回Integer.MAX_VALUE或MAX_ARRAY_SIZE,取决于所需的最小容量是否大于MAX_ARRAY_SIZE。
    */
    private static int hugeCapacity(int minCapacity) {
   
   
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
add()、addAll()

和 C++ 的 vector 不一样,ArrayList 没有 push_back() 方法,,对应的方法是 add(E e) , ArrayList 也没有insert()方法,对应的方法是 add(int index, E e)

这两个方法都是向容器中添加新元素,这可能会导致capacity不足,因此在添加元素

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值