Java集合类总结 (二)
LinkedList类
由于基于数组的链表有一个大的缺点,那就是从链表中间移除一个元素时需要将此元素后面的所有元素向前移动,会产生大量的开销,同样的在链表中间插入一个新元素也会有大量开销。如下图:
Linkedlist由于其实现方式使用的是指针,或者说是引用,那么就不存在arraylist那种插入或者移除一个元素时的大开销情况了:
当然,LinkedList与ArrayList都是保持元素顺序的,调用add的先后顺序决定了最终遍历的顺序:
List<String> staff = new LinkedList<>();
staff.add("Amy");
staff.add("Bob");
staff.add("Carl");
ListIterator<String> iter = staff.listIterator();
iter.next(); // skip past first element
iter.add("Juliet");
LinkedList与ArrayList提供listIterator方法,返回一个实现了ListIterator接口的对象,此接口提供了Iterator接口一些没有的功能,是为链表定制的遍历器:
当多次调用add方法后,元素会被按顺序加到当前遍历器位置之前:
当刚刚调用完listIterator后没有调用next,直接调用add方法后,新加入的元素会插入到第一个位置,作为list的头结点;
当一直调用next方法,直到hasNext方法返回false,也就是到达list尾部时,在这是调用add方法将新加入的元素作为list的最后结点,即新的尾结点。
当list中存在n个元素时,那么就会有n+1个空位可以插入新的结点,如下面的list有A、B、C三个元素:
|ABC
A|BC
AB|C
ABC|
分别对应上面4个空位点可以插入新的元素。
listIterator中还包含一个set方法,它会替换刚刚由next或者previous返回的元素,也就是遍历器刚刚经过的那个元素:
ListIterator<String> iter = list.listIterator();
String oldValue = iter.next(); // returns first element
iter.set(newValue); // sets first element to newValue
当有两个iterator同时在操作一个list的时候,就有可能产生冲突。比如一个iterator在删除元素,另一个再读取元素。list本身可以检查到这种情况,如果有多于一个iterator在操作list,就会抛出ConcurrentModificationException异常。如下代码:
List<String> list = . . .;
ListIterator<String> iter1 = list.listIterator();
ListIterator<String> iter2 = list.listIterator();
iter1.next();
iter1.remove();
iter2.next(); // throws ConcurrentModificationException
- 为了避免这种冲突,需要遵从如下原则:
- 可以给一个list附上多个iterator,但是它们都只有只读权限;
- 或者只给list附一个iterator,它可读可写。
LinkedList是不支持快速随机访问的,如果需要获取第n个元素,那么唯一的方法就是从LinkedList的第一个节点开始遍历,当然,这样很费时间,所以当需要类似获取第n个元素的操作时,不要使用LinkedList。
使用LinkedList的唯一原因是其能够快速的进行插入与删除操作,而不损耗时间,如果你只有少量的元素进行存储,还是选择arraylist吧。
建议远离LinkedList中使用integer index去获取和操作list的所有方法,如果需要随机访问集合,请使用arrayList。
ArrayList类
List实现了两种访问方式,一种是通过iterator从头访问到尾, 一种是通过get和set方法直接访问list中的元素。linkedlist属于前者,arraylist属于后者。
ArrayList内部封装了一个可以动态改变大小的数组。
Vector与ArrayList的不同在于Vector是线程安全的,多个线程同时操作时安全的。而ArrayList则不是。当然Vector为了维持多线程同步安全,牺牲了部分性能。