一、概述
List遍历是我们经常会使用到的一个功能,很多时候我们也会用到反向遍历,在用到反向遍历时我们常用的方法是通过for循环或者是对List反向排序来完成相应的操作,像下面代码这样:
方法1:
for(int i = list.size()-1; i>=0; i--) {
System.out.println(list.get(i));
}
这个方法开上去就比较臃肿,写起来也相对繁琐一些。
方法2:
Collections.reverse(list);
System.out.println(list);
这种方法看似不错,但是reverse改变了原list的结构,如果我们只是要反向遍历list而不想改变list的结构的话这个方法是不适用的,除非你想在遍历完list之后再次调用reverse把list变回去,这种做法当然是不值得推荐的。
那么有没有一种方法可以既不改变原有的List的结构,有可以很简洁的实现反向遍历呢?我个人认为,直接制作一个支持反向遍历的List就可以很好的解决这个问题。下面我们就从头开始制作支持反向遍历List。
二、准备工作
众所周知,所有的Collection都实现了Iterable接口,而Iterable接口中的iterator()方法就是用来实现集合类遍历的,那么我们是不是可以从这里开始入手。
还有一点,就是为什么要基于ArrayList去做这件事而不是直接实现List接口,原因很简单,如果我要实现List接口那么我就需要实现List接口中所有的方法,而我比较懒,不想再去实现List接口中的所有方法,而且一般需要遍历处理的都是ArrayList,所有我就直接在ArrayList的基础上去做这件事,也省了不少功夫。
三、新建List
话不多说,开干。首先我们需要定义一个类,并且这个类要继承ArrayList:
public class ErgodicList<T> extends ArrayList<T> {
/*构造方法 start*/
public ErgodicList() {
super();
}
public ErgodicList(Collection<? extends T> integers) {
super(integers);
}
/*构造方法 end*/
}
接着我们要编写能够实现反向遍历的Interator:
/**
* 反向遍历Iterator
*
* @return Iterable
*/
public Iterable<T> reversed() {
return () -> new Iterator<T>() {
int index = ErgodicList.super.size() - 1;
@Override
public boolean hasNext() {
return index > -1;
}
@Override
public T next() {
return ErgodicList.super.get(index--);
}
// Java8以下版本需要重写该方法
// @Override
// public void remove() {
// throw new UnsupportedOperationException("remove");
// }
};
}
从代码可以看出,首先我们要取出List最末尾的Index,这一点跟for循环很像,然后就是重写Iterator接口的两个方法(java8种remove()有了默认方法,我这里也不需要它所以就没有重写),一个用来判断是否还有下一个元素,一个用来获取元素。
到此可以支持反向遍历的List就编写好了,是不是感觉比for循环还要复杂,但是这属于一劳永逸的方法,麻烦一次以后再也不用苦逼的写for循环了。下面我们来测试一下效果如何:
public static void main(String[] args) {
ErgodicList<Integer> integers = new ErgodicList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
System.out.println("------------反向遍历结果-------------");
integers.reversed().forEach(System.out::println);
System.out.println("------------------------------------");
System.out.println(integers);
}
从结果可以看出,简单的foreach代码在不改变原有List的结构的情况下(废话Interator当然不会改变List的结构)实现了List的反向遍历,并且以后再需要反向遍历的时候我们只需要new一个我们自制的List再加一个forEach就好了,用起来相当方便。
四、衍生
我们既然实现了反向遍历,那么可不可以实现随机遍历呢,当然没问题,举一反三是我们程序员应有的基本素养。下面来看看随机遍历的实现,还是刚才的那个List,我们再增加一个方法:
/**
* 随机遍历Iterator
*
* @return Iterable
*/
public Iterable<T> random() {
return () -> {
List<T> randomList = new ArrayList<>(this);
Collections.shuffle(randomList, new Random());
return randomList.iterator();
};
是不是更简单,具体的说明我就不写了(我比较懒),有兴趣的朋友可以自己查一下。测试代码和反向遍历的测试也差不多,只需要:
integers.random().forEach(System.out::println);
使用起来体验也是很不错的,大家可以试一试。
附完整代码,以便参考:
------------------------------------------------------------------------------