每日一类-java.util.List

本文深入探讨了集合类型,包括ArrayList和LinkedList的特点与实现。解析了动态扩容、子集合、序列化等核心概念,并提供了示例代码。

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

1. 简介

集合类型,不定长的"数组",支持对“数组”进行灵活的操作。
实现上,有基于数组的集合(ArrayList)和基于链表的集合(LinkedList)。

2. 概念

  1. 动态扩容
  2. MAX_ARRAY_SIZE,最大数组长度=Integer.MAX_VALUE - 8
  3. 双向链表之Node,快捷方式之firstlast
  4. 简单的二分法:node(int index)
  5. 双向队列

3. 源码

  • ArrayList
  1. toArray(T[] a)
    // 保证new的a的长度,要能cover住原list的大小。
    // 否则需要使用反射构造一个新的数组

    if (a.length < size)
       // Make a new array of a's runtime type, but my contents:
       return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    
    // codes in Arrays.copyOf
    Array.newInstance(newType.getComponentType(), newLength);
    
  2. 动态扩容calculateCapacity
    // 默认构造函数生成的list,第一次add,容量扩大到10
    // 后续按1.5倍大小扩容

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
  3. 子集合subList(from, to)

    // 返回的是SubList,非ArrayList
    // 引用的父集合的数据,操作子集合,结果会体现在父集合上。
    return new SubList(this, 0, fromIndex, toIndex);
    
  4. 序列化

    // 1. 空集合初始化,元素数组是DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    // 2. 序列化,读到空集合,返回元素集合为 EMPTY_ELEMENTDATA
        private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;
        ...
        }
        
    // 3. 如此,空集合在经过序列化处理后,不能正常扩容。见第2点:calculateCapacity
    
  • LinkedList
  1. 简洁的Node

    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;
       }
    }
    
  2. 简单的二分法

    node(index) {
    	if (index < (size >> 1)) {
    		for (int i = 0; i < index; i++) ...
    	}
    	else {
    		for (int i = size - 1; i > index; i--) ...
    	}
    }
    
  3. toArray or Iterator
    避免并发的影响,相关的操作对象,都是先“实例化”再处理

    // 1. 集合转数组
    addAll(index, collection c) {
    	Object[] a = c.toArray(); // 避免add过程中,c出现并发修改,导致addAll报错
    }
    
    // 2. first, last
    xxx() {
    	final Node<E> f = first;
    }
    

4. 示例

  1. toString

    @Test
    public void testList2String() {
        List<String> list = new ArrayList<String>(3 );
        list.add("1");
        list.add("3");
        list.add("2");
    
        Assert.assertEquals("[1, 3, 2]", list.toString());
    
        List<String> linklist = new LinkedList<String>();
        linklist.add("1");
        linklist.add("3");
        linklist.add("2");
    
        Assert.assertEquals("[1, 3, 2]", linklist.toString());
    }
    
  2. 序列化后扩容失效

        @Test
    public void testAdd2EmptyList() throws IOException, ClassNotFoundException {
        List<String> list = new ArrayList<String>();
        // size = max(1.5*size, size+1);
        // elementData.length = 10
        list.add("1");
        Assert.assertEquals(1, list.size());
    
        list = new ArrayList<String>();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(list);
    
        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(in);
        List<String> anoList = (List<String>) ois.readObject();
    
        // elementData.length = 1, not 10
        anoList.add("2");
        // elementData.length = 2
        anoList.add("3");
    }
    
  3. 子集合

        @Test
    public void testSubList() {
        List<String> pList = new ArrayList<String>(10);
        for (int i = 0; i < 10; i++) {
            pList.add("" + i);
        }
    
        // getSubList
        List<String> subList = pList.subList(2,3);
        System.out.println(subList);
        subList.add("bug");
    
        Assert.assertEquals("bug", pList.get(3));
        System.out.println(pList);
    }
    
  4. Add All

    // java.util.ConcurrentModificationException
        @Test
    public void testAddAll() {
        final List<String> ll = new LinkedList<>();
        String sayHi = "Hello all, this is a test for linked list.";
        for (String s : sayHi.split("[,.\\s]+")) {
            ll.add(s);
        }
    
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i =0; i< 100; i++) {
                    ll.add(i+"");
                }
            }
        }).start();
        List<String> anoll = new LinkedList<>();
    //        anoll.addAll(ll);  // 此时,新集合的长度就已经固定了
        for (String s : ll) {
            anoll.add(s);
        }
        System.out.println(anoll.size());
    }
    

5. 总结

集合 or 数组? ArrayList or LinkedList ?
LinkedList通过增加“复杂”的数据结构,实现了高性能的随机插入和删除操作。
同时它也没有“容量”的概念,空间不需要统一分配。
对于“不定长”的,变化快的数据,LinkedList是个不错的选择?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值