Java集合------List集合

一、List接口

    List接口直接继承Collection接口,代表有序的Collection。

    public interface List extends Collection {}

    是一个有序的允许重复的集合,可以通过索引查找list中的具体元素。在开发中常用实现类有:ArrayList、LinkedList、Vector:

      ArrayList 是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。

      LinkedList 是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随机插入、随机删除效率低。

      Vector 是矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的

  1.常用的方法    

    add():在列表的最后添加元素;

    add(index,obj):将指定的元素插入此列表中的指定位置;

    size():返回当前列表的元素个数

    get(int index):返回下标为index的元素;

    set(index,obj):用新传入的对象,将指定位置的元素替换掉,返回被替换前的元素对象;

    remove():传入一个下标,或者一个对象,删除指定元素;

          如果删除指定对象,需要重写equals()方法,比对传入的对象是不是属于原列表,如果属于,结果返回true;

    clear():清除列表中所有元素;

    isEmpty():检测列表是否为空;返回true/false;

    contains():传入一个对象,检测列表中是否含有该对象;

    indexOf():传入一个对象,返回该对象在列表中首次出现的地址;

    lastInsevOf():传入一个对象,返回该对象在列表中最后一次出现的地址;

    subList( int fromIndex , int toIndex) :截取一个子列表返回List类型;

    toArray():将列表转为数组,返回一个Object[]类型;

    iterator():使用迭代器遍历。

  2.List使用场景

    应当根据实际操作场景和需要具体考虑选用List的实现类 (涉及“栈” 、“队列” 、“链表”等):

      (1)需要快速插入、删除元素,应该使用LinkedList, LinkedList是有序的,增删快、查询慢

      (2)对于需要快速随机访问元素,应使用ArrayList,ArrayList是无序的,查询块、增删慢

      (3)对于“单线程环境” 或者:“多线程环境,但List仅仅只会被单个线程操作”,此时应该使用非同步的类,如ArrayList

           Vector是支持同步的,Stack继承于Vector。

 

二、ArrayList集合

    ArrayList是一个动态数组,elementDate就是底层数组,其初始的容量为10 ,也就是未定义时数组的大小。随着数组中元素的不断增加,数组的容量的大小也会随

  着增加。在每次向容器中增加元素的同时,会对ArrayList进行容量检查,当快溢出时,就会进行扩容操作。在定义数组时,一般要定义数组的初始值,减少因元素个素

  超过数组初始大小时进行扩容操作而浪费时间、效率。

    图解:

              Java集合------List集合_第1张图片

    ArrayList的size()isEmpty()get(int index)set(int index, E element)iterator() 和 listIterator()操作都是以固定时间运行。add()操作以分摊

  的固定时间运行,添加n个元素需要O(n)时间,要考虑到扩容,所以时间不仅仅只是添加元素分摊固定时间。

    ArrayList类似于数组,存储的数据在内存中是连续的、成块的、查找的时候直接遍历内存就可以;而插入删除的时候,就要把修改的那个节点之后的所有数据都向

  后移,或者向前移。

  1.线程不安全

     ArrayList 是基于动态数组的集合。ArrayList查询速度非常快,使得它在实际开发中被广泛使用,但它并不是线程安全的。

      
// 查询元素
public E get(int index) {
    rangeCheck(index);                  // 检查是否越界
    return elementData(index);
}
// 顺序添加元素
public boolean add(E e) {
    ensureCapacityInternal(size + 1);   // 扩容机制
    elementData[size++] = e;
    return true;
}
// 从数组中间添加元素
public void add(int index, E element) {
    rangeCheckForAdd(index);            // 数组下标越界检查
    ensureCapacityInternal(size + 1);   // 扩容机制
    System.arraycopy(elementData, index, elementData, index + 1, size - index); // 复制数组
    elementData[index] = element;       // 替换元素
    size++;
}
// 从数组中删除元素
private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    elementData[--size] = null; // clear to let GC do its work
}
View Code

     由于ArrayList是非同步的,没有同步锁;在多线程的情况下,调用这个方法时,可能会有可能被多个线程拿到相同的size值去与ArrayList的容量做比较,而执行到

   elemntData[size++] = e;时却是有序的,这时由于没有适当的扩大ArrayList的容量,从而导致插入数据的长度大于ArrayList的剩余容量,于是抛出了数组越界异常

   (java.lang.ArrayIndexOutOfBoundsException)。

      一个ArrayList,在添加一个元素的时候,它有可能会分为两步来完成:

      (1)在Items[size]的位置存放此元素

      (2)增大Size值

      在单线线程运行的情况下,如果size=0,添加一个元素后,此元素在位置0,而且size=1;

      而如果在多线程情况下,比如有两个线程,线程A先将元素存放在位置0。但是此时CPU调度线程A暂停,线程B得到运行的机会。线程B也向此ArrayList添加元素,

    因为此时size仍然等于0(假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都

    增加 Size 的值。现在看ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。 

      关于ArrayList线程不安全更详细的可以参考:  https://blog.csdn.net/qq_28081081/article/details/80413669

   线程不安全的解决:

    如果需要在多线程中使用,可以采用Collections.synchronizedList()方法

    List> data=Collections.synchronizedList(new ArrayList>());

三、LinkedList集合

   LinkedList是List接口的链表实现,首尾相接,可以作为栈、队列、双端队列数据结构使用,本质是双向链表,不能像ArrayList一样随意访问查询;包含两个重要的

   成员:header 和 size。header是双向链表的表头,它是双向链表节点所对应的类Entry的实例。Entry中包含成员变量: previous, next, element。其中,previous是

   该节点的上一个节点,next是该节点的下一个节点,element是该节点所包含的值。size是双向链表中节点的个数。LinkedList非同步,线程不安全.

                Java集合------List集合_第2张图片

   查询慢、增删快

   LinkedList 其实是一个个的Node节点,每个Node节点首位相连

    
// 查询元素
public E get(int index) {
    checkElementIndex(index);   // 检查是否越界
    return node(index).item;
}
Node node(int index) {
    if (index < (size >> 1)) {  // 类似二分法
        Node x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}
// 插入元素
public void add(int index, E element) {
    checkPositionIndex(index);  // 检查是否越界
    if (index == size)          // 在链表末尾添加
        linkLast(element);
    else                        // 在链表中间添加
        linkBefore(element, node(index));
}
void linkBefore(E e, Node succ) {
    final Node pred = succ.prev; 
    final Node newNode = new Node<>(pred, e, succ);
    succ.prev = newNode;
    if (pred == null)   
        first = newNode;
    else                
        pred.next = newNode;
    size++;
    modCount++;
}
View Code

  当进行数组循环时,ArrayList以及数组,适合用for循环下标遍历,当用for大批量的循环LinkedList时,程序会卡死

  LinkedList适合用foreach循环        (队列 : 先进先出     栈:先进后出)  

四、Vector

   Vector的数据结构和ArrayList相似,操作基本与ArrayList一致,但是其对数据操作的方法基本上都被synchronized关键字修饰,所以是线程安全的,所以相比较

  于ArrayList而言,性能要低。若想要一个高性能,又是线程安全的ArrayList,可以使用Collections.synchronizedList(list),方法或者使用CopyOnWriteArrayList集合。

你可能感兴趣的:(Java集合------List集合)