Java中List的简述

Posted 努力创造BUG

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中List的简述相关的知识,希望对你有一定的参考价值。

快速通道

什么是 List

什么是集合

集合就是把具有相同属性的东西放在一起,也可以是容器,把有关的东西都放进去

什么是 List

List是位于java.util下的一个接口,有序集合(也称为序列)。此界面的用户可以精确控制每个元素在列表中的插入位置。用户可以通过整数索引(列表中的位置)访问元素,并在列表中搜索元素

List的继承、实现关系

其继承了Collection接口并由AbstractList来实现,Collection又继承了Iterable接口

  • Collection:集合层次结构中的根接口。一个集合表示一组对象,称为它的元素。一些集合允许重复元素,而另一些则不允许。有些是有序的,有些是无序的。
  • Iterable:实现此接口允许对象成为“for-each 循环”语句的目标。

List类的位置

jre\\lib\\rt.jar\\java\\util 下
也就是java本身自带的,不需要额外引用jar来实现

List的种类

  • ArrayList:底层由数组结构实现Object[],可以存储任何Object类型的对象,是非线程安全的
  • LinkedList:List和Deque接口的双向链表实现。实现所有可选列表操作,并允许所有元素(包括null )。所有操作都按照双向链表的预期执行。索引到列表中的操作将从开头或结尾遍历列表,以更接近指定索引的为准。
  • Vector:底层实现是动态数组的方式存放数据,是线程安全的,Vector源码当中每个方法都被synchronized关键字进行修饰,保证了Vector的线程安全,所以效率很低,尽量少使用

ArrayList

使用

        List list = new ArrayList<>();        
        List<Object> list1= new ArrayList<>();
        List<Map> list2= new ArrayList<>();
        List<Student> list3= new ArrayList<>();
        List<Object> list4 = new ArrayList<>(2);
        List<Object> list5 = new ArrayList<>(list);
        ...

实现方式

创建对象的时候会调用其无参构造方法

/**
 * Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;


/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = ;




/**
 * 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
    



/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() 
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;





/**
 * 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 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
 */
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;
    



可见,当创建ArrayList对象的时候就会创建好一个初始容量为 10 的空列表等待着被使用。

如何扩容



private void ensureExplicitCapacity(int minCapacity) 
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);



/**
 * 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
 */
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);



int newCapacity = oldCapacity + (oldCapacity >> 1);
每次自增一半,即原来的1.5倍

LinkedList

使用


        List list = new LinkedList<>();
        List<Object> list1 = new LinkedList<>();
        List<Map> list2 = new LinkedList<>();
        List<Student> list3 = new LinkedList<>();
        List<Object> list4 = new LinkedList<>(list);
        ...

实现方式

创建对象的时候会调用其无参构造方法



/**
 * Constructs an empty list.
 */
public LinkedList() 




/**
 * 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
 */
public LinkedList(Collection<? extends E> c) 
    this();
    addAll(c);



/**
 * Appends all of the elements in the specified collection to the end of
 * this list, in the order that they are returned by the specified
 * collection's iterator.  The behavior of this operation is undefined if
 * the specified collection is modified while the operation is in
 * progress.  (Note that this will occur if the specified collection is
 * this list, and it's nonempty.)
 *
 * @param c collection containing elements to be added to this list
 * @return @code true if this list changed as a result of the call
 * @throws NullPointerException if the specified collection is null
 */
public boolean addAll(Collection<? extends E> c) 
    return addAll(size, c);


LinkedList初始化为空

如何扩容

由于LinkedList使用双向列表实现的其结构为

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;
    

因此也没有扩容的机制,如有需求一直在其前面或者后面新增下去

Vector

使用

        Vector vector = new Vector<>();
        Vector<Object> vector1 = new Vector<>();
        Vector<Map> vector2 = new Vector<>();
        Vector<Student> vector3 = new Vector<>();
        ...

实现方式



/**
 * The array buffer into which the components of the vector are
 * stored. The capacity of the vector is the length of this array buffer,
 * and is at least large enough to contain all the vector's elements.
 *
 * <p>Any array elements following the last element in the Vector are null.
 *
 * @serial
 */
protected Object[] elementData;



/**
 * The amount by which the capacity of the vector is automatically
 * incremented when its size becomes greater than its capacity.  If
 * the capacity increment is less than or equal to zero, the capacity
 * of the vector is doubled each time it needs to grow.
 *
 * @serial
 */
protected int capacityIncrement;



/**
 * Constructs an empty vector so that its internal data array
 * has size @code 10 and its standard capacity increment is
 * zero.
 */
public Vector() 
    this(10);




/**
 * Constructs an empty vector with the specified initial capacity and
 * with its capacity increment equal to zero.
 *
 * @param   initialCapacity   the initial capacity of the vector
 * @throws IllegalArgumentException if the specified initial capacity
 *         is negative
 */
public Vector(int initialCapacity) 
    this(initialCapacity, 0);


/**
 * Constructs an empty vector with the specified initial capacity and
 * capacity increment.
 *
 * @param   initialCapacity     the initial capacity of the vector
 * @param   capacityIncrement   the amount by which the capacity is
 *                              increased when the vector overflows
 * @throws IllegalArgumentException if the specified initial capacity
 *         is negative
 */
public Vector(int initialCapacity, int capacityIncrement) 
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;


可以看出Vector和ArrayList是非常相似的

  • 都是一个Object[]类型存储数据
  • 无参数构造默认数组容量是10
  • 使用有参数的构造方法可以指定容量的大小

如何扩容


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);


int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
每次自增一倍,即原来的2倍

常用方法

共有

  • int size();
    返回此列表中的元素数
  • boolean isEmpty();
    如果此列表不包含任何元素,则返回true
  • boolean contains(Object o);
    如果此列表包含指定元素,则返回true
  • Iterator iterator();
    以正确的顺序返回此列表中元素的迭代器
  • Object[] toArray();
    返回此列表中从第一个元素到最后一个元素的数组
  • boolean add(E e);
    将指定元素附加到此列表的末尾
  • boolean remove(Object o);
    如果指定元素存在,则从该列表中删除第一次出现的指定元素。如果此列表不包含该元素,则它不变
  • boolean addAll(Collection<? extends E> c);
    将指定集合中的所有元素附加到此列表的末尾,按照指定集合的​​迭代器返回的顺序
  • boolean removeAll(Collection<?> c);
    从此列表中删除包含在指定集合中的所有元素
  • void clear();
    从此列表中删除所有元素。此调用返回后,列表将为空
  • boolean equals(Object o);
    比较指定对象与此列表是否相等。当且仅当指定对象也是一个列表时返回true ,两个列表具有相同的大小,并且两个列表中所有对应的元素对都相等
  • E get(int index);
    返回此列表中指定位置的元素
  • E set(int index, E element);
    将此列表中指定位置的元素替换为指定元素
  • void add(int index, E element);
    在此列表中的指定位置插入指定元素。将当前位于该位置的元素和任何后续元素向右移动
  • E remove(int index);
    移除此列表中指定位置的元素
  • int indexOf(Object o);
    返回此列表中指定元素第一次出现的索引,如果此列表不包含该元素,则返回 -1
  • int lastIndexOf(Object o);
    返回此列表中指定元素最后一次出现的索引,如果此列表不包含该元素,则返回 -1

LinkedList

  • E getFirst();
    返回此列表中的第一个元素
  • E getLast();
    返回此列表中的最后一个元素
  • E removeFirst();
    从此列表中删除并返回第一个元素
  • E removeLast();
    移除并返回此列表中的最后一个元素
  • void addFirst(E e);
    在此列表的开头插入指定元素
  • void addLast(E e);
    将指定元素附加到此列表的末尾
  • boolean hasNext();
    如果此列表迭代器在向前遍历列表时具有更多元素,则返回true
  • E next();
    返回列表中的下一个元素并前进光标位置

结尾

  • 上述内容绝大多数来自源码
  • 部分为自己总结

记:反复使用后的小总结

java基础-容器简述

常用的容器有list、queue、set、map

list有ArrayList、LinkedList,还有一个CopyOnWriteArrayList;

queue有LinkedList、ArrayQueue、LinkedBlockingQueue、ConcurrentLinkedQueue;

map有HashMap、TreeMap、ConcurrentHashMap、ConcurrentSkipListMap;

set内部一般会使用map做存储,有HashSet、TreeSet、CopyOnWriteArraySet、ConcurrentSkipListSet;

 

之前介绍过ConcurrentHashMap,该map由于需要resize,所以需要锁机制,且加锁粒度较大;Blocking系列由于需要wait、notify操作,所以也需要锁机制;ConcurrentLinkedQueue则是完全的CAS操作。

 

List

ArrayList是一个数组,在插入元素可能涉及扩容、元素移动,在删除时可能涉及元素移动,如果只是在list尾部插入删除,就不会涉及元素移动,其优势在于随机访问。

LinkedList是一个链表,每次插入元素都会新建一个该元素的节点,然后放入链表中,与ArrayList相比,优势就在于插入、删除操作不会涉及元素移动,只涉及前后引用的改变,同样也不存在扩容问题,链表理论可以无限大(实际长度不能超过Integer.MAX_VALUE,且受限于jvm堆空间)。

CopyOnWriteArrayList是一个数组,与ArrayList不同之处在于,CopyOnWriteArrayList在更新操作前都会新建一个数组(该数组大小正好容下新旧元素),然后在新数组进行更新操作,最后再将旧数组替换掉,并且整个更新过程都是加锁的(ReentrantLock),即是线程安全的。可见,CopyOnWriteArrayList的更新操作的性能不是一般的差啊,但是其优势在于iterator是完全安全的,因为更新操作永远是在一个新数组上进行,而iterator操作永远都是旧数组,但也因为iterator操作的是旧数组,所以CopyOnWriteArrayList的iterator不提供更新操作(如remove)。

Queue

ArrayQueue比ArrayList要高效一些,因为其将更新元素操作限制在了数组的头尾,在插入删除时不再需要移动元素(插入时仍然会涉及扩容)。

LinkedList不仅实现了list接口,同样还实现了queue、deque(double ended queue)接口。

LinkedBlockingQueue提供了一个阻塞的双向队列,可以实现在队列为空、为满时等待,非空、非满时唤醒,这是通过ReentrantLock+Condition实现的,也就是说LinkedBlockingQueue是线程安全的(虽然LinkedBlockingQueue为链式结构,但其在构造器中提供了capacity的设置,默认capacity为Integer.MAX_VALUE)。

ConcurrentLinkedQueue提供了线程安全的队列实现,与LinkedBlockingQueue不同的是ConcurrentLinkedQueue并没有使用锁机制,使用的是CAS机制,这样可以极大提高并发性。

Map

HashMap的hash表是一个数组,解决hash冲突的方式是链表或红黑树(jdk8)。

TreeMap是一棵红黑树。

ConcurrentHashMap结构与HashMap类似,通过在桶上加锁实现了线程安全。

ConcurrentSkipListMap使用了跳表,跳表的物理结构为多层链表,逻辑结构是一棵树,与ConcurrentHashMap相比,ConcurrentSkipListMap的最大特点就是key有序。

Set

CopyOnWriteArraySet包装了CopyOnWriteArrayList,CopyOnWriteArraySet的add方法就是CopyOnWriteArrayList的addIfAbsent方法,同样是线程安全的。

以上是关于Java中List的简述的主要内容,如果未能解决你的问题,请参考以下文章

java基础-容器简述

Java中List和ArrayList的区别

java中 List 与Set 的区别

Java中的Set,List,Map的区别

java中 List 与Set 有啥区别?

java的这三种for循环有啥区别?