java语言基础--集合学习,ArrayList和Linkedlist

Collection<E>接口

  这个接口是集合框架最顶级的接口,该接口扩展了Iterable接口,这意味着所有的集合类fore-each风格进行遍历。

ArrayList 与 Linkedlist

区别:

  ArrayList是实现了基于动态数组,LinkedList基于链表。对于随机访问get和set,ArrayList性能要优于LinkedList,因为LinkedList要移动指针。对于删除和新增LinkedList性能要优于ArrayList,因为ArrayList要移动数据。 

ArrayList的扩容方式,扩容时机

  当集合中的元素超出容量,便会进行扩容操作。扩容操作也是ArrayList 的一个性能消耗比较大的地方,所以若我们可以提前预知数据的规模,应该通过public ArrayList(int initialCapacity) {}构造方法,指定集合的大小,去构建ArrayList实例,以减少扩容次数,提高效率

ArrayList的成员属性

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
    private static final long serialVersionUID = 8683452581122892189L;
    //默认初始容量
    private static final int DEFAULT_CAPACITY = 10;
    //默认构造函数的空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    //瞬态(在采用Java默认的序列化机制的时候,被该关键字修饰的属性不会被序列化)的数组,真正存放元素的数组
    transient Object[] elementData; // non-private to simplify nested class access
    //elementData存放元素的数量,这里和容量不一样
    private int size;
}

 ArrayList的构造方法

    public ArrayList(int initialCapacity) {
        super();//即父类protected AbstractList() {}
        if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
        this.elementData = new Object[initialCapacity];//创建一个容量为initialCapacity的空的(但是size==0)对象数组
    }

    public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;//将空数组赋值给elementData
    }

    public ArrayList(Collection<? extends E> c) {
        //调用Collection.toArray()方法得到一个对象数组,并赋值给elementData
        elementData = c.toArray();
        //设置size的值
        size = elementData.length;
        //c.toArray()如果没有返回Object[]时,利用Arrays.copyOf 来复制集合c中的元素到elementData数组中
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }

add方法

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;//将数组元素追加到末尾,并修改size
        return true;
    }
    
    private void ensureCapacityInternal(int minCapacity) {
        //根据EMPTY_ELEMENTDATA  判断数组是否是用默认构造函数初始化的,(这里不考虑ArrayList(Collection<? extends E> c)这种情况是因为,minCapacity是肯定大于c的size的)
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
    
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;///如果确定要扩容,会修改modCount 
        
        // 如果传入的数大于数组原容量 则开始扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    private void grow(int minCapacity) {
        //
        int oldCapacity = elementData.length;
        //扩容为原来的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //取1.5倍的容量和传入的扩容系数的最大值
        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);
    }
    //最大返回 Integer.MAX_VALUE
    private static int hugeCapacity(int minCapacity) {
        // overflow
        if (minCapacity < 0) throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
    }

指定位置插入add(int index, E element)

    public void add(int index, E element) {
        rangeCheckForAdd(index);//范围检查

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //将index开始的数据 向后移动一位
        System.arraycopy(elementData, index, elementData, index + 1,size - index);
        elementData[index] = element;
        size++;
    }
    
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

addAl方法

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        //扩容准备
        ensureCapacityInternal(size + numNew);  
        // 复制数组 ,并载入数据
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

查询

    public E get(int index) {
        rangeCheck(index);//范围检查

        return elementData(index);//下标获取数据
    }    

    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

 Linkedlist

    transient int size = 0;//集合元素数量

    transient Node<E> first; //头部节点

    transient Node<E> last;//尾部节点

构造方法

   public LinkedList() {}

    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);//将集合c所有元素插入链表中
    }

内部类 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;
        }
    }

add方法

    //尾部插入新节点
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    
    void linkLast(E e) {
        final Node<E> l = last;//记录原来的尾部节点
        final Node<E> newNode = new Node<>(l, e, null);//以原尾部节点为前节点生成新节点
        last = newNode;//将当期节点设置为尾部节点
        if (l == null)//如果原尾部节点为null  则将当前节点设置为链表的首节点
            first = newNode;
        else//否则将当前节点设置为原尾部节点的后节点
            l.next = newNode;
        size++;
        modCount++;
    }

其他例子就不看了,总得来说 LinkedList  修改数据时,只需要设置新节点和关联前后节点关系即可,不向ArrayList那样,增加一个数据。后面的数据都要后移。因此LinkedList 对数据操作效率高。

猜你喜欢

转载自www.cnblogs.com/tjqBlog/p/9822307.html
今日推荐