博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

JavaEE - 11集合Collection-List

Posted on 2020-12-07 22:08  Kingdomer  阅读(118)  评论(0编辑  收藏  举报

Javaee - 11集合Collection-List

(4)Collection子接口:List接口

(4.1)List接口概述

  • 鉴于数组存储数据的局限性,通常使用List替代数组。List-> "动态"数组。
  • List集合类中元素有序、可重复,集合中的每个元素都有其对应的顺序索引
  • List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
  • JDK API中List接口的实现类有: ArrayList、 LinkedList 和 Vector。三者异同。
    • ArrayList: List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData存储
    • LinkedList:对于频繁插入删除操作,效率比ArrayList高;底层使用双向列表存储
    • Vector: List接口的古老实现类;线程安全,效率低;底层使用Object[] elementData存储

(4.2)ArrayList

(4.2.1)JDK 7下源码分析

  • ArrayList list = new ArrayList(); //底层创建初始长度为10的Object[]数组elementData
  • list.add(123);  // elementData[0] = new Integer(123);
  • ......
  • list.add(11);  // 如果此次添加导致底层elementData数组容量不够,则扩容默认情况下,扩容为原来的容量的1.5倍,同时将原有数组中的数据复制到新的数组中
  • 建议: 开发中使用带参数的构造器: ArrayList list = new ArrayList(int capacity);  节省扩容的性能开支

(4.2.2)JDK 8中的变化

  • ArrayList list = new ArrayList(); //底层Object[]数组elementData初始化为{},不创建长度10的数组
  • list.add(123);  // 第一次调用add时,底层创建长度10的数组,添加元素elementData[0] = new Integer(123);
  • 后续的添加和扩容操作与JDK7一样。
  • JDK7对象的创建类似于单例的饿汉式,JDK8的ArrayList对象创建类似于单例的懒汉式,延迟了数组创建,节省内存。

(4.2.3)重要源码(JDK8)

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    public ArrayList() {
        // 不带参数,初始化为空数组
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

 

    private static final Object[] EMPTY_ELEMENTDATA = {};
    // 带参数初始化
    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);
        }
    }

 

    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // 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 ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(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)  // MAX_ARRAY_SIZE = Integer.MAX_VALUE -8
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

 

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

(4.3)LinkedList

  • linkedList 是一个双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。
  • 定义内部类Node,作为LinkedList中保存数据的基本结构。
  • Node除了保存数据,还定义了两个变量: prev变量记录前一个元素的位置,next变量记录下一个元素的位置
  • 没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增Node。 

对于双向链表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;
        }
    }

 

在列表尾部插入一个元素

  • 先将列表最后一个元素last 保存为Node节点 l;
  • 组装一个新的Node节点newNode, Node 的 prev 是 l, 值为e, next 为null。
  • 将新的节点newNode 作为列表的最后一个元素。
  • 将原来元素的 next 指向 newNode新节点。 如果是第一个元素,则把 first 也指向新节点。
  • 列表 size 加1, modCount 加1。
    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++;
    }

    void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        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++;
    }

(4.4)Vector

JDK7 和 JDK8 中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。在扩容方面,默认扩容为原来的数组长度的2倍。

    public Vector() {
        this(10);
    }

    public synchronized void ensureCapacity(int minCapacity) {
        if (minCapacity > 0) {
            modCount++;
            ensureCapacityHelper(minCapacity);
        }
    }

    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

(4.5)List接口

除了继承Collection的方法,List添加了一些根据索引来操作集合元素的方法。

  • void add(int index, Object ele):            在index位置插入ele元素
  • boolean addAll(int index, Collection eles): 从index位置开始将eles中的所有元素添加进去。
  • Object get(int index):                      获取指定index位置的元素
  • int indexOf(Object obj):                    返回obj在集合中首次出现的位置
  • int lastIndexOf(Object obj):                返回obj在当前集合中末次出现的位置
  • Object remove(int index):                   移除指定index位置的元素,并返回此元素
  • Object set(int index, Object ele):          设置指定index位置的元素为ele
  • List subList(int fromIndex, int toIndex):   返回从fromIndex到toIndex位置的子集合。
    @Test
    public void test1(){
        ArrayList list = new ArrayList();
        list.add(123);
        list.add("acd");
        list.add(new Person("Tom",12));

        System.out.println(list);                 //[123, acd, Person@27dd1a]
        list.add(2,"dddd");
        System.out.println(list);                 //[123, acd, dddd, Person@27dd1a]
        System.out.println(list.get(2));          //dddd
        System.out.println(list.set(2,"DDDD"));   //dddd
        System.out.println(list);                 //[123, acd, DDDD, Person@27dd1a]
    }

区分List中的remove(int index) 和 remove(Object obj) // remove(2) remove(new Integer(2))

    @Test
    public void testListRemove(){
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        updateList(list);
        System.out.println(list); //[1,2]
    }

    private static void updateList(List list){
        list.remove(2);   // index位置
    }

 

   @Test
    public void testListRemove(){
        List list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        updateList(list);
        System.out.println(list);  // [1,3]
    }

    private static void updateList(List list){
        list.remove(new Integer(2));   // 删除元素为2
    }