ArrayList和LinkedList
List ADT(抽象数据类型)有两种流行的实现方式。
ArrayList:
提供了List ADT的一种可增长数组的实现。使用ArrayList的优点在于,对get和set的调用花费常数时间。其缺点是新项的插入和现有相的删除代价昂贵,除非变动的是在ArrayList的末端。看下面代码,add和remove方法,当指定位置添加或者删除一个元素的时候,指定位置以及之后的元素都会后移或者前移。
1 public interface List<AnyTipe> extends Collection<AnyTipe> { 2 AnyType get(int idx); 3 AnyType set(int idx, AnyType newVal); 4 5 //在指定下标插入元素,原位置以及之后的元素全部后移 6 void add(int idx, AnyType x); 7 8 void remove(int idx); 9 10 ListIterator<AnyType> listIterator( int pos); 11 }
LinkedList:
提供了List ADT的双链表实现。使用LinkedList的优点在于,新项的插入和现有项的删除均开销很小,假设变动项的位置是已知的。意味着在表的前端进行插入和删除是常数时间的。LinkedList的缺点是不容易操作索引,因此对get的调用是昂贵的,除非调用非常近的端点。
eg:remove方法对LinkedList类的使用:
题目:将一个表中所有具有偶数值的项删除
想法:
1、构造一个包含所有奇数的新表,然后清除原表,并将这些奇数拷贝回原表。
2、直接在原表上操作,遍历原表,发现奇数项,直接将其删除
问题:
ArrayList插入删除花费昂贵,显然是一个比较失败的策略。LinkedList却是存在着某种希望,因为我们知道,在已知位置的删除操作都可以通过重新安排某些链而被有效的完成。
解法:
1 /** 2 * 这种方式产生的问题: 3 * 1、LinkedList中的get方法,调用的效率不高 4 * 2、remove方法调用的Collection接口的方法,效率低下 5 * @param list 6 */ 7 public static void removeVal1(List<Integer> list) { 8 int i = 0; 9 while(i < list.size()) { 10 if(list.get(i) % 2 == 0) { 11 list.remove(i); 12 }else { 13 i++; 14 } 15 } 16 }
1 /** 2 * 1、使用迭代器一步步的遍历该表,取代get,这样做是高效的。 3 * 2、但是我们使用的是Collection接口的remove方法,这么做不是高效的,因为remove方法必须再次搜索该项。 4 * 3、运行程序回发现一个问题:java.util.ConcurrentModificationException 5 * 当一项被删除时,由于增强的for循环使用的基础迭代是非法的。 6 * 我们不能期待增强for循环懂得只有当一项不被删除时,他才必须向前推进。 7 * 8 * @param list 9 */ 10 public static void removeVal2(List<Integer> list) { 11 for(Integer item : list) { 12 if(item % 2 == 0) { 13 list.remove(item); 14 } 15 } 16 }
/** * 迭代器的remove方法花费常数的时间,因为该迭代器位于需要被删除的节点位置。 * @param list */ public static void removeVal3(List<Integer> list) { Iterator<Integer> itra = list.iterator(); while(itra.hasNext()) { Integer item = itra.next(); if(item % 2 == 0) { itra.remove(); } } }