ArrayList 以数组实现。节约空间,但数组有容量限制。超出限制时会增加50%容量,用System.arraycopy()复制到新的数组,因此最好能给出数组大小的预估值。默认第一次插入元素时创建大小为10的数组。 按数组下标访问元素--get(i)/set(i,e) 的性能很高,这是数组的基本优势。 直接在数组末尾加入元素--add(e)的性能也高,但如果按下标插入、删除元素--add(i,e), remove(i),remove(e),则要用System.arraycopy()来移动部分受影响的元素,性能就变差了,这是基本劣势。 LinkedList 以双向链表实现。链表无容量限制,但双向链表本身使用了更多空间,也需要额外的链表指针操作。 按下标访问元素--get(i)/set(i,e) 要悲剧的遍历链表将指针移动到位(如果i>数组大小的一半,会从末尾移起)。 插入、删除元素时修改前后节点的指针即可,但还是要遍历部分链表的指针才能移动到下标所指的位置,只有在链表两头的操作--add(), addFirst(),removeLast()或用iterator()上的remove()能省掉指针的移动。 CopyOnWriteArrayList 并发优化的ArrayList。用CopyOnWrite策略,在修改时先复制一个快照来修改,改完再让内部指针指向新数组。 因为对快照的修改对读操作来说不可见,所以只有写锁没有读锁,加上复制的昂贵成本,典型的适合读多写少的场景。如果更新频率较高,或数组较大时,还是Collections.synchronizedList(list),对所有操作用同一把锁来保证线程安全更好。 增加了addIfAbsent(e)方法,会遍历数组来检查元素是否已存在,性能可想像的不会太好。 补充 无论哪种实现,按值返回下标--contains(e), indexOf(e), remove(e) 都需遍历所有元素进行比较,性能可想像的不会太好。 没有按元素值排序的SortedList,在线程安全类中也没有无锁算法的ConcurrentLinkedList,凑合着用Set与Queue中的等价类时,会缺少一些List特有的方法。
|