• 数据结构(集合)学习之List


    集合

    框架关系图:

    Collection接口下面有三个子接口:List、Set、Queue。此篇是关于List<E>的简单学习总结。

     补充:HashTable父类是Dictionary,不是AbstractMap。

    List(有序、可重复):

    List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。

    List常用的子类:ArrayList。(面试常问的:Vector、ArrayList、LinkedList之间的区别)。

    ArrayList:

    public class ArrayList<E> extends AbstractList<E> implements List<E> (继承AbstractList类,实现List接口)

    • 方法摘要(标黄为常用方法)

      Modifier and Type方法描述
      void add​(int index, E element)
      在此列表中的指定位置插入指定的元素。
      boolean add​(E e)
      将指定的元素追加到此列表的末尾。
      boolean addAll​(int index, Collection<? extends E> c)
      将指定集合中的所有元素插入到此列表中,从指定的位置开始。
      boolean addAll​(Collection<? extends E> c)
      按指定集合的Iterator返回的顺序将指定集合中的所有元素追加到此列表的末尾。
      void clear​()
      从列表中删除所有元素。
      Object clone​()
      返回此 ArrayList实例的浅拷贝。
      boolean contains​(Object o)
      如果此列表包含指定的元素,则返回 true
      void ensureCapacity​(int minCapacity)
      如果需要,增加此 ArrayList实例的容量,以确保它至少能够容纳最小容量参数指定的元素数量。
      void forEach​(Consumer<? super E> action)
      Iterable每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。
      E get​(int index)
      返回此列表中指定位置的元素。
      int indexOf​(Object o)
      返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
      boolean isEmpty​()
      如果此列表不包含元素,则返回 true
      Iterator<E> iterator​()
      以正确的顺序返回该列表中的元素的迭代器。
      int lastIndexOf​(Object o)
      返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
      ListIterator<E> listIterator​()
      返回列表中的列表迭代器(按适当的顺序)。
      ListIterator<E> listIterator​(int index)
      从列表中的指定位置开始,返回列表中的元素(按正确顺序)的列表迭代器。
      E remove​(int index)
      删除该列表中指定位置的元素。
      boolean remove​(Object o)
      从列表中删除指定元素的第一个出现(如果存在)。
      boolean removeAll​(Collection<?> c)
      从此列表中删除指定集合中包含的所有元素。
      boolean removeIf​(Predicate<? super E> filter)
      删除满足给定谓词的此集合的所有元素。
      protected void removeRange​(int fromIndex, int toIndex)
      从此列表中删除所有索引为 fromIndex (包括)和 toIndex之间的元素。
      void replaceAll​(UnaryOperator<E> operator)
      将该列表的每个元素替换为将该运算符应用于该元素的结果。
      boolean retainAll​(Collection<?> c)
      仅保留此列表中包含在指定集合中的元素。
      E set​(int index, E element)
      用指定的元素替换此列表中指定位置的元素。
      int size​()
      返回此列表中的元素数。
      void sort​(Comparator<? super E> c)
      根据指定的Comparator引发的顺序排列此列表。
      Spliterator<E> spliterator​()
      在此列表中的元素上创建late-binding故障快速 Spliterator
      List<E> subList​(int fromIndex, int toIndex)
      返回指定的 fromIndex (含)和 toIndex之间的列表部分的视图。
      Object[] toArray​()
      以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。
      <T> T[] toArray​(T[] a)
      以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。
      void trimToSize​()
      修改这个 ArrayList实例的容量是列表的当前大小。 

    用法:

     1     public static void main(String[] args) {
     2         ArrayList<String> list = new ArrayList<String>();
     3         System.out.println(list.isEmpty());// 方法:isEmpty​(),如果此列表不包含元素,则返回 true。
     4         list.add(0, "A");// 方法:add​(int index, E element),在此列表中的指定位置插入指定的元素。给第一个位置加元素:A
     5         list.add(1, "B");// 给第二个位置加元素:B
     6         list.add("C");//方法:add​(E e), 将指定的元素追加到此列表的末尾。 继续追加元素:C
     7         list.add("D");//继续追加元素:D
     8         // 此时遍历一下,看看list的值是否成功加入。
     9         for (String value : list) {// 方法:forEach​(Consumer<? super E> action),对Iterable每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。注意:此时使用Iterator迭代器遍历数值。
    10             System.out.println(value);//输出:A B C D,成功加入数值
    11         }
    12         System.out.println(list.size());//方法:size​() 返回此列表中的元素数。  
    13         System.out.println(list.isEmpty());// 此时再用方法判断一下,因为已经有值所以返回False
    14         System.out.println(list.contains("A"));// 方法:contains​(Object o),如果此列表包含指定的元素,则返回 true 。
    15         System.out.println(list.get(2));// 方法: get​(int index) 返回此列表中指定位置的元素。
    16         System.out.println(list.indexOf("B"));// 方法:indexOf​(Object o) 返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
    17         System.out.println(list.indexOf("E"));// 此时返回:-1
    18         System.out.println("--------分割线--------");
    19         list.remove(2);// 方法:remove​(int index) 删除该列表中指定位置的元素。
    20         // 此时遍历一下,看看list的值是否成功删除。
    21         for (String value : list) {
    22             System.out.println(value);
    23         }
    24         System.out.println("--------分割线--------");
    25         list.remove("D");// 方法:remove​(Object o) 从列表中删除指定元素的第一个出现(如果存在)。
    26         // 此时遍历一下,看看list的值是否成功删除。
    27         /*注意List中的remove方法不能和foreach()连用,因为foreach使用Iterator迭代,和list中的方法无法识别,
    28         如果用list.remove()删除元素,会导致list数值和长度大小都发生改变,但此时Iterator无法知道此时,于是就会报错:ConcurrentModificationException*/
    29         for (String value : list) {
    30             System.out.println(value);
    31         }
    32         System.out.println("--------分割线--------");
    33         //为了后面演示方法,此时再假如几个值
    34         list.add("C");
    35         list.add("D");
    36         list.add("e");
    37         for (String value : list) {
    38             System.out.println(value);
    39         }
    40         System.out.println("--------分割线--------");
    41         list.set(4, "E");
    42         for (String value : list) {
    43             System.out.println(value);
    44         }
    45     }

    补充一:ArrayList底层其实是动态数组,长度可变,源代码如下:

     1    
     2 private static final Object[] EMPTY_ELEMENTDATA = {};
     3 public boolean add(E e) {
     4         ensureCapacityInternal(size + 1);  // Increments modCount!!
     5         elementData[size++] = e;
     6         return true;
     7     }
     8     public void add(int index, E element) {
     9         rangeCheckForAdd(index);
    10 
    11         ensureCapacityInternal(size + 1);  // Increments modCount!!
    12         System.arraycopy(elementData, index, elementData, index + 1, size - index);
    13         elementData[index] = element;
    14         size++;
    15     }

    补充二:ArrayList初始容量是:10,超过这个容量会扩容,扩容规则为:数组需要最小容量+(数组需要最小容量/2),即扩容 1.5倍,源代码如下:

     1     private static final int DEFAULT_CAPACITY = 10;
     2     public boolean add(E e) {
     3         ensureCapacityInternal(size + 1); 
     4         elementData[size++] = e;
     5         return true;
     6     }
     7     private void ensureCapacityInternal(int minCapacity) {
     8         if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
     9             minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    10         }
    11 
    12         ensureExplicitCapacity(minCapacity);
    13     }
    14     private void ensureExplicitCapacity(int minCapacity) {
    15         modCount++;
    16 
    17         if (minCapacity - elementData.length > 0)
    18             grow(minCapacity);
    19     }
    20     private void grow(int minCapacity) {
    21         int oldCapacity = elementData.length;
    22         int newCapacity = oldCapacity + (oldCapacity >> 1);
    23         if (newCapacity - minCapacity < 0)
    24             newCapacity = minCapacity;
    25         if (newCapacity - MAX_ARRAY_SIZE > 0)
    26             newCapacity = hugeCapacity(minCapacity);
    27         elementData = Arrays.copyOf(elementData, newCapacity);
    28     }

    ArrayList、Vector、LinkedList:

    Linkedlist:

    Linkedlist和ArrayList、Vector不一样的地方是,Linkedlist不是采用数组结构,而是链状结构,着重操作首位元素和末位元素,源代码如下:

     1     transient Node<E> first;
     2     transient Node<E> last;
     3 
     4     public void addFirst(E e) {
     5         linkFirst(e);
     6     }
     7     private void linkFirst(E e) {
     8         final Node<E> f = first;
     9         final Node<E> newNode = new Node<>(null, e, f);
    10         first = newNode;
    11         if (f == null)
    12             last = newNode;
    13         else
    14             f.prev = newNode;
    15         size++;
    16         modCount++;
    17     }
    18     public void addLast(E e) {
    19         linkLast(e);
    20     }
    21     void linkLast(E e) {
    22         final Node<E> l = last;
    23         final Node<E> newNode = new Node<>(l, e, null);
    24         last = newNode;
    25         if (l == null)
    26             first = newNode;
    27         else
    28             l.next = newNode;
    29         size++;
    30         modCount++;
    31     }

    因为LinkedList着重操作首尾元素,所以增和删相对来说比较高效,而ArrayList和Vector底层是动态数组,存在数组索引(index),所以改和查比较高效。

    Vector(已过期,基本不再使用):

    Vector和ArrayList,LinkedList不同的地方是,Vector是线程安全的List,因为它源代码中的方法基本都加上了关键词synchronized:

     1     public synchronized void addElement(E obj) {
     2         modCount++;
     3         ensureCapacityHelper(elementCount + 1);
     4         elementData[elementCount++] = obj;
     5     }
     6     public synchronized void insertElementAt(E obj, int index) {
     7         modCount++;
     8         if (index > elementCount) {
     9             throw new ArrayIndexOutOfBoundsException(index
    10                                                      + " > " + elementCount);
    11         }
    12         ensureCapacityHelper(elementCount + 1);
    13         System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
    14         elementData[index] = obj;
    15         elementCount++;
    16     }
    17     public synchronized boolean removeElement(Object obj) {
    18         modCount++;
    19         int i = indexOf(obj);
    20         if (i >= 0) {
    21             removeElementAt(i);
    22             return true;
    23         }
    24         return false;
    25     }
    26     public synchronized E set(int index, E element) {
    27         if (index >= elementCount)
    28             throw new ArrayIndexOutOfBoundsException(index);
    29 
    30         E oldValue = elementData(index);
    31         elementData[index] = element;
    32         return oldValue;
    33     }
    34     public synchronized E get(int index) {
    35         if (index >= elementCount)
    36             throw new ArrayIndexOutOfBoundsException(index);
    37 
    38         return elementData(index);
    39     }

     有没有其他线程安全的List?

    1、java.util.Collections.SynchronizedList

     1        public static <T> List<T> synchronizedList (List < T > list) {
     2             return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : new SynchronizedList<>(list));
     3         }
     4         SynchronizedList(List < E > list) {
     5             super(list);
     6             this.list = list;
     7         }
     8         public E get ( int index){
     9             synchronized (mutex) {
    10                 return list.get(index);
    11             }
    12         }
    13         public E set ( int index, E element){
    14             synchronized (mutex) {
    15                 return list.set(index, element);
    16             }
    17         }
    18         public void add ( int index, E element){
    19             synchronized (mutex) {
    20                 list.add(index, element);
    21             }
    22         }
    23         public E remove ( int index){
    24             synchronized (mutex) {
    25                 return list.remove(index);
    26             }
    27         }

    1、Collection是集合的根接口,Collections是一个Java.util 包下的工具类。

    2、Collections.synchronizedList返回的List和Vector一样,在方法上都有synchronized关键字加了锁,让线程变得安全,但同时性能会下降。

    2、CopyOnWriteArrayList

    CopyOnWriteArrayList是Java1.5加入的一个线程安全的变体ArrayList ,其中所有可变操作( add , set ,等等)通过对底层数组的最新副本实现。实现方法是:先复制一份List,然后上锁,然后进行增删改查的操作,操作过后再解锁。

     1     private transient volatile Object[] array;
     2     final Object[] getArray () {
     3             return array;
     4         }
     5     public CopyOnWriteArrayList() {
     6             setArray(new Object[0]);
     7         }
     8     public CopyOnWriteArrayList(E[]toCopyIn){
     9             setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
    10         }
    11         public boolean add (E e){
    12             final ReentrantLock lock = this.lock;
    13             lock.lock();
    14             try {
    15                 Object[] elements = getArray();
    16                 int len = elements.length;
    17                 Object[] newElements = Arrays.copyOf(elements, len + 1);
    18                 newElements[len] = e;
    19                 setArray(newElements);
    20                 return true;
    21             } finally {
    22                 lock.unlock();
    23             }
    24         }
    25         public E set ( int index, E element){
    26             final ReentrantLock lock = this.lock;
    27             lock.lock();
    28             try {
    29                 Object[] elements = getArray();
    30                 E oldValue = get(elements, index);
    31 
    32                 if (oldValue != element) {
    33                     int len = elements.length;
    34                     Object[] newElements = Arrays.copyOf(elements, len);
    35                     newElements[index] = element;
    36                     setArray(newElements);
    37                 } else {
    38                     // Not quite a no-op; ensures volatile write semantics
    39                     setArray(elements);
    40                 }
    41                 return oldValue;
    42             } finally {
    43                 lock.unlock();
    44             }
    45         }
    46         public E remove ( int index){
    47             final ReentrantLock lock = this.lock;
    48             lock.lock();
    49             try {
    50                 Object[] elements = getArray();
    51                 int len = elements.length;
    52                 E oldValue = get(elements, index);
    53                 int numMoved = len - index - 1;
    54                 if (numMoved == 0)
    55                     setArray(Arrays.copyOf(elements, len - 1));
    56                 else {
    57                     Object[] newElements = new Object[len - 1];
    58                     System.arraycopy(elements, 0, newElements, 0, index);
    59                     System.arraycopy(elements, index + 1, newElements, index,
    60                             numMoved);
    61                     setArray(newElements);
    62                 }
    63                 return oldValue;
    64             } finally {
    65                 lock.unlock();
    66             }
    67         }
    68         public E get ( int index){
    69             final ReentrantLock lock = l.lock;
    70             lock.lock();
    71             try {
    72                 rangeCheck(index);
    73                 checkForComodification();
    74                 return l.get(index + offset);
    75             } finally {
    76                 lock.unlock();
    77             }
    78         }
  • 相关阅读:
    MySQL数据库导入方法(最佳方案--cmd命令行方式导入,不会导致数据结构或表丢失)
    MySQL:互联网公司常用分库分表方案汇总!
    主流的消息中间件有哪些?
    Java实现进制之间转换的工具类
    python查找文件夹下所有指定后缀名的文件
    Visual Studio检查内存泄露方法
    Linux重要文件被删恢复问题
    docker 安装与相关操作
    C++ 替换路径中斜杠 并获取完整路径的文件名
    catch2:一个好用的C++单元测试框架
  • 原文地址:https://www.cnblogs.com/Bernard94/p/12316277.html
Copyright © 2020-2023  润新知