数据结构(集合)学习之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 }