Collections.reverse 代码思考-超越昨天的自己系列(13)
点进Collections.reverse的代码瞄了眼,然后就开始了一些基础知识的收集。
现在发现知道的越多,知道不知道的越多。
列几个记录下:
reverse方法源码:
/** * Reverses the order of the elements in the specified list.<p> * * This method runs in linear time. * * @param list the list whose elements are to be reversed. * @throws UnsupportedOperationException if the specified list or * its list -iterator does not support the <tt>set</tt> operation. */ public static void reverse (List<?> list) { int size = list.size(); if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) { for ( int i=0, mid=size>>1, j=size-1; i<mid; i++, j--) swap(list, i, j); } else { ListIterator fwd = list.listIterator(); ListIterator rev = list.listIterator(size); for ( int i=0, mid=list.size()>>1; i<mid; i++) { Object tmp = fwd.next(); fwd.set(rev.previous()); rev.set(tmp); } } }
1,首先看见RandomAccess
将操作随机访问列表的最佳算法(如 ArrayList)应用到连续访问列表(如 LinkedList)时,可产生二次项的行为。如果将某个算法应用到连续访问列表,那么在应用可能提供较差性能的算法前,鼓励使用一般的列表算法检查给定列表是否为此接口的一个 instanceof,如果需要保证可接受的性能,还可以更改其行为。
现在已经认识到,随机和连续访问之间的区别通常是模糊的。例如,如果列表很大时,某些 List 实现提供渐进的线性访问时间,但实际上是固定的访问时间。这样的 List 实现通常应该实现此接口。实际经验证明,如果是下列情况,则 List 实现应该实现此接口,即对于典型的类实例而言,此循环:
for (int i=0, n=list.size(); i < n; i++) list.get(i);
的运行速度要快于以下循环:
for (Iterator i=list.iterator(); i.hasNext(); ) i.next();
2,标记接口(marker interface)
例如,如果没有使用Iterator,遍历一个数组的方法是使用索引:
for(int i=0; i<array.size(); i++) { ... get(i) ... }
客户端都必须事先知道集合的内部结构,访问代码和集合本身是紧耦合,无法将访问逻辑从集合类和客户端代码中分离出来,每一种集合对应一种遍历方法,客户端代码无法复用。
更恐怖的是,如果以后需要把ArrayList更换为LinkedList,则原来的客户端代码必须全部重写。
为解决以上问题,Iterator模式总是用同一种逻辑来遍历集合:
for(Iterator it = c.iterater(); it.hasNext(); ) { ... }
奥秘在于客户端自身不维护遍历集合的"指针",所有的内部状态(如当前元素位置,是否有下一个元素)都由Iterator来维护,而这个Iterator由集合类通过工厂方法生成,因此,它知道如何遍历整个集合。
ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator 没有此功能。
public static void main(String[] args) { List list =new LinkedList(); for (int i = 0; i < 100000; i++) { list.add(i); } int size = list.size(); long t1 = System.currentTimeMillis(); for ( int i=0, mid=size>>1, j=size-1; i<mid; i++, j--) swap(list, i, j); long t2 = System.currentTimeMillis(); System.out.println(t2 - t1);//结果1 long t3 = System.currentTimeMillis(); ListIterator fwd = list.listIterator(); ListIterator rev = list.listIterator(size); for ( int i=0, mid=list.size()>>1; i<mid; i++) { Object tmp = fwd.next(); fwd.set(rev.previous()); rev.set(tmp); } long t4 = System.currentTimeMillis(); System.out.println(t4 - t3);//结果2 } public static void swap(List<?> list, int i, int j) { final List l = list; l.set(i, l.set(j, l.get(i))); }
那么ArrayList 使用这两种方式的效果呢?
测试结果相差无几,随着增大数据量,swap要好于ListInterator,但是有时微乎其微,所以这个reverse代码中并没有对大数据量的ArrayList进行swap方式,减少了代码冗余,也没有降低什么性能。
---------------------------------------
还有很多扩展学习的地方,继续前进吧。