Java集合之List
来源:互联网 发布:ubuntu 共享打印机 编辑:程序博客网 时间:2024/05/23 16:55
Java中最常用的三种集合类型: List,Set, Map, Queue
List最常用的两种实现:
- ArrayList
- LinkedList
Iterable && Iterator
Iterable接口表示可通过迭代器进行访问,通过迭代器访问的好处是将容器本身的行为和元素访问分开,将对所有容器的访问抽象出来,形成共同的方法,既方便理解,也方便使用。Iterator仅能单向访问容器,提供了获取可用元素、删除刚获取元素以及判断容器中是否还有可用元素的方法。
//获取容器iteratorpublic interface Iterable<T> { Iterator<T> iterator();}public interface Iterator<E> { boolean hasNext(); E next(); void remove();}
Collection
Collection接口继承自Iterable, 表示容器可通过iterator进行访问,Collection指明集合提供哪些功能,一般无外乎元素的增删改查
public interface Collection<E> extends Iterable<E> { // Query Attribute int size(); boolean isEmpty(); //query boolean contains(Object o); //visit Iterator<E> iterator(); Object[] toArray(); <T> T[] toArray(T[] a); // Modification Operations boolean add(E e); boolean remove(Object o); void clear(); // Bulk Operations boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); boolean removeAll(Collection<?> c); boolean retainAll(Collection<?> c); //inherit form Object boolean equals(Object o); int hashCode();}
增删指增删某个元素及删除所有,查包括查询自身属性,是否包含某个元素,更改一般是set方法,Collection中无更改方法,此外,还包括集合与集合之间操作,判断是否是子集,求并集,求差集,求交集。为方便使用数组的特性和方法,还提供了转化为数组的操作。
AbstractCollection
Collection有一个抽象实现AbstractCollection,借助迭代器实现了一些简单的方法,继承类根据自身数据存储访问特点,如果有更高效的实现会重写这些方法。一般Collection接口的子类都继承自AbstractCollection.
//Collection提供的通过toString方法,将集合中元素以逗号分隔连接,[]括起来public String toString() { Iterator<E> it = iterator(); if (! it.hasNext()) return "[]"; StringBuilder sb = new StringBuilder(); sb.append('['); for (;;) { E e = it.next(); sb.append(e == this ? "(this Collection)" : e); if (! it.hasNext()) return sb.append(']').toString(); sb.append(',').append(' '); }}
List
List继承关系图:
List Interface继承自Collection,由于List内部是按照插入顺序有序的,List接口除了继承自Collection的方法外,还增加了按索引index的增删改查,相比Collection,提供了修改方法。
public interface List<E> extends Collection<E> { // Query Operations int size(); boolean isEmpty(); boolean contains(Object o); Iterator<E> iterator(); Object[] toArray(); <T> T[] toArray(T[] a); // Modification Operations boolean add(E e); boolean remove(Object o); // Bulk Modification Operations boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); boolean removeAll(Collection<?> c); boolean retainAll(Collection<?> c); void clear(); // Comparison and hashing boolean equals(Object o); int hashCode(); //List自有 // Positional Access Operations E get(int index); E set(int index, E element); void add(int index, E element); boolean addAll(int index, Collection<? extends E> c); E remove(int index); // Search Operations int indexOf(Object o); int lastIndexOf(Object o); // List Iterators ListIterator<E> listIterator(); ListIterator<E> listIterator(int index); // View List<E> subList(int fromIndex, int toIndex);}
ListIterator
Java中List是双向List, 通用Iterator仅能单向访问容器,因此List接口提供了获取ListIterator的方法,ListIterator扩展自通用Iterator,通过ListIterator可双向访问List,可添加,删除,更改容器,以及获取Iterator的Index.
public interface ListIterator<E> extends Iterator<E> { // Query Operations,双向迭代 boolean hasNext(); E next(); boolean hasPrevious(); E previous(); //迭代器index int nextIndex(); // 获取刚刚通过previous()元素的index int previousIndex(); //获取刚刚通过next()访问元素的index // Modification Operations,修改List void remove(); void set(E e); void add(E e);}
此外,List接口的subList方法返回List本身子集的一个视图,返回的view的底层数据依然是List的数据,只是可以更直观的访问操作子集。
AbstactList
List接口有一个抽象实现AbstactList, 实现了List接口中的一些通用功能,因为List接口继承自Collection,因此,AbstactList直接利用AbtractCollection的一些实现,继承自AbtractCollection, 同时优化了AbtractCollection的一些方法,这样实现List接口的容器就可以直接继承自AbstactList。
ArrayList
故名思议,ArrayList就是用数组实现的List,组数是内存连续元素集合,可通过索引快速随机访问。
通过上图可看出ArrayList extend AbstractList, implements List、RandomAccess, Cloneable和Serializable四个接口, 其中RandomAccess, Cloneable、Serializable三个接口都是标记接口,没有任何方法。
既然是用数组实现,数组大小是固定的,就面临一个问题,当List中元素个数超过固定大小时,数组需要动态扩容,并将原内容copy至扩容后的组数中。JDK实现中默认初始大小是10,以1.5倍速率动态扩容。
//内部数组private transient Object[] elementData;//初始容量大小private static final int DEFAULT_CAPACITY = 10; private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; //以1.5倍速率扩容 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);}
ArrayList内部通过一个Object[]数组保存对象,这样通过index可以直接定位元素,批量操作移动也可借助System.arraycopy native方法高效实现。
LinkedList
LinkedList内部元素之间通过链表的形式组织,只能顺序访问List,此外链表可以方便的在头尾部添加删除获取元素,因此JDK实现中为LinkedList实现了Deque接口,添加了双向队列的功能。
//指向头结点transient Node<E> first;//指向尾结点transient Node<E> last;//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; }}
Iterator FailFast
当对某个容器进行迭代时,不允许除当前迭代器以外的方式改变容器的内部结构,例如其他的线程改变容器结构,这样可以快速告诉访问者底层发生了改变,而不是让访问者继续进行当前的操作,以致后续发生更严重或莫名奇妙的错误。
Java中迭代器快速失败是通过抛出ConcurrentModificationException体现的,但是此
行为无法得到保证,即使底层结构真正发生改变,也不一定每次都会抛出此异常,因为很难探测到每一次结构更改,因此不能依赖于是否抛出ConcurrentModificationException来进行错误纠正,仅可用于检测bug,即如果有ConcurrentModificationException,则可认为程序有bug。java.util包下的集合类都是Fail Fast.
ArrayList中是如何实现Iterator FailFast的呢?通过源码发下是在每次获取Iterator时在
迭代器内部保存了当前List的修改次数,每次调用next获取元素是都会检测Iterator私有的expectedModCount是否与List内部保存的modCount相等,若不相等则抛出ConcurrentModificationException。详情参考:
https://my.oschina.net/hosee/blog/612718
Vector
从ArrayList/LinkedList实现可以看出,它们并非线程安全的,没有任何同步操作,当有多个线程同时add一个List时,可能会发生数据丢失。实现List接口的Vector是线程安全的,从源码可以看出是在主要的实现方法上添加synchronized关键字,直接在this上加锁,get时也加锁,这样效率较低。
//vector addpublic synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true;}//vector getpublic synchronized E get(int index) { if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); return elementData(index);}
CopyOnWriteArrayList
java.util.concurrent.CopyOnWriteArrayList也线程安全的实现了的List,其直接implements List,没有继承自AbstractList.
CopyOnWriteArrayLis主要思想就是在有修改内部数组操作时copy出一个新数组,对新数组进行修改操作,修改完成后再将新数组的引用赋值给List内部数组变量,这样原来的迭代还是基于原始内存数组,不受影响。
//CopyOnWriteArrayList.addpublic boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; //copy出一个新数组 Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; //保存新数组 setArray(newElements); return true; } finally { lock.unlock(); }}
注意每次add时都会加锁,这样才能保证所有线程add的数据不会丢失。
相对Fail-Fast, CopyOnWriteArrayLis的迭代器COWIterator是Fail-Safe的,迭代时并不会抛出ConcurrentModificationException,其对原结构的修改是在其copy上进行的,不会被原结构的迭代器感知,这样带来的代价就是每次更改时copy需要额外的空间和时间上的开销,可能产生大量的对象,此外,更改前COWIterator获取的是旧数据,不能保证遍历的是最新的内容。
SynchronizedList
如果更新频率较高,或数组较大时,用Collections.SynchronizedList(List),对所有操作用同一把锁来保证线程安全也许是更好的选择。
static class SynchronizedList<E> extends SynchronizedCollection<E> implements List<E> { private static final long serialVersionUID = -7754090372962971524L; final List<E> list; SynchronizedList(List<E> list) { super(list); this.list = list; } SynchronizedList(List<E> list, Object mutex) { super(list, mutex); this.list = list; } public boolean equals(Object o) { if (this == o) return true; synchronized (mutex) {return list.equals(o);} } public int hashCode() { synchronized (mutex) {return list.hashCode();} } public E get(int index) { synchronized (mutex) {return list.get(index);} } public E set(int index, E element) { synchronized (mutex) {return list.set(index, element);} } public void add(int index, E element) { synchronized (mutex) {list.add(index, element);} } public E remove(int index) { synchronized (mutex) {return list.remove(index);} } public int indexOf(Object o) { synchronized (mutex) {return list.indexOf(o);} } public int lastIndexOf(Object o) { synchronized (mutex) {return list.lastIndexOf(o);} } public boolean addAll(int index, Collection<? extends E> c) { synchronized (mutex) {return list.addAll(index, c);} } public ListIterator<E> listIterator() { return list.listIterator(); // Must be manually synched by user } public ListIterator<E> listIterator(int index) { return list.listIterator(index); // Must be manually synched by user } public List<E> subList(int fromIndex, int toIndex) { synchronized (mutex) { return new SynchronizedList<>(list.subList(fromIndex, toIndex), mutex); } } private Object readResolve() { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : this); }}
Stack
我们知道stack是后进先出的结构,主要提供push、pop、peek方法,直接上源码简单直接,其继承自Vector,因此也是线程安全的List.
publi cclass Stack<E> extends Vector<E> { public Stack() { } public E push(E item) { addElement(item); return item; } public synchronized E pop() { E obj; int len = size(); obj = peek(); removeElementAt(len - 1); return obj; } public synchronized E peek() { int len = size(); if (len == 0) throw new EmptyStackException(); return elementAt(len - 1); } public boolean empty() { return size() == 0; } public synchronized int search(Object o) { int i = lastIndexOf(o); if (i >= 0) { return size() - i; } return -1; } /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = 1224463164541339165L;}
- Java集合之List集合
- JAVA集合之List
- Java集合之List
- Java集合之List
- Java集合之List
- java集合之List
- java 集合之List
- Java集合之List
- Java之List集合
- Java之集合List
- Java集合之List
- Java集合之List
- 四大名捕-----JAVA集合之List
- java之List,Set集合
- java集合框架之list
- java集合类之List
- java集合框架之List
- java学习之List集合
- synchronized锁住的是代码还是对象
- 程序员的算法趣题Perl版 (二)
- 第五节、模块:css文件打包
- 包与类的命名
- ScrollView中嵌套ListView滑动问题
- Java集合之List
- JAVA代码优化技巧
- Opencv3.1.0+VS2013(Win7 64位)
- TCP
- 第九周项目三
- React Native View组件实例
- android 全屏显示
- JS异步读取二进制信息
- 35 个 Java 代码性能优化总结