Java集合总结一[List篇]

ArrayList特点是查询快但增删慢,适合读多写少的场景,其底层为Object数组,扩容策略为1.5倍。LinkedList适用于头尾操作,中间插入删除效率低,是双向链表实现,无扩容操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ArrayList

特点

查询快

底层数据存储空间连续, 可以采用数组下标方法进行数据获取,数组+下标可以计算得到对应元素在内容的地址, CPU可以直接访问

增删慢

涉及到元素数据移动,移动操作浪费时间

增加操作可能导致底层数组扩容, 扩容操作需要时间和空间

删除操作导致有效元素个数和数组容量比例失衡, 导致缩容操作

数据结构

        Object[]数组

        非静态数组, 每个ArrayList都有自己的私有成员变量


    transient Object[] elementData; // non-private to simplify nested class access

构造方法

        默认容量:  int DEFAULT_CAPACITY = 10;

        无参默认空数组:  Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

        有参默认空数组:  Object[] EMPTY_ELEMENTDATA = {};

    //无参构造方法
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
   
    //初始化列表容量
    //initialCapacity – 列表的初始容量
    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);
        }
    }

    
    //初始化列表
    //c – 其元素要放入此列表的集合
    public ArrayList(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }

添加元素

        可能会移动元素 O(n)

        可能会扩容 (1.5倍)

注意: 要分配的数组的最大大小。某些 VM 会在数组中保留一些标头字。尝试分配更大的阵列可能会导致内存不足错误:请求的阵列大小超过 VM 限制 

        MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    //核心代码是扩容
    //minCapacity – 所需的最小容量
    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);
    }
    //扩容1.5倍
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ? //int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

扩容流程

1. 明确最小方法流程

2. 得到源数组需求

3. 计算得到新数组容量

        int new Capacity = oldCapacity + (oldCapacity >> 1);

4. 判断新数组容量是否满足最小容量需求

5. 判断新数组容量是否超出最大容量范围

6. 根据源数组数据, 新数组容量, 调用copyOf方法创建新数组, 返回新数组地址

删除元素

       可能需要移动元素 0(n)

       缩容, 无自动缩容机制,需要[手动缩容]

    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

查找元素

        时间复杂度O(1)

    public E get(int index) {
        rangeCheck(index);  //index是否合法

        return elementData(index);
    }

适用场景

        有参构造, 读操作 >> 写操作

已知数据容量,可以在实例化 ArrayList 集合对象时,明确当前数据的容量存储范围,采用尾插法方式将数据逐一放入到集合中,对于数组元素增删慢弊端就可以避免,方便后期作为数组搜索,排序,查询等相关操作。

LinkedList

特点

头尾节点操作效率高

中间操,涉及节点遍历查询效率低, 导致整体性能差

数据结构

        双向链表

    private static class Node<E> {
        E item; //当前节点数据
        Node<E> next; //下一个节点对象
        Node<E> prev; //上一个节点对象
    }
    //头节点对象
    transient Node<E> first;
    //尾节点对象
    transient Node<E> last;

构造方法

    //无参列表
    public LinkedList() {
    }
    //有参列表
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c); ==> addAll(size, c)
    }

添加元素

        没有扩容操作

        头插法和尾插法

                头: addFirst(E)  

                尾: add(E)  addAll( Collection<E> )  addLast(E)

    //头插法
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }
    //尾插法
    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++;
    }
    //add(int index, E element)的核心
    //查找
    Node<E> node(int index) {
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
    //插入
    void linkBefore(E e, Node<E> succ) {
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

删除元素       

    E unlink(Node<E> x) {
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

查找元素

        时间复杂度O(n)

    Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值