LinkedList 可以使用升序或降序迭代器进行迭代,如下所示:
LinkedList<Object> list = new LinkedList<Object>();
...
StringJoiner sJ1 = new StringJoiner(" ");
list.iterator().forEachRemaining(a -> sJ1.add(a.toString()));
System.out.println("averse: \n" + sJ1.toString());
StringJoiner sJ2 = new StringJoiner(" ");
list.descendingIterator().forEachRemaining(a -> sJ2.add(a.toString()));
System.out.println("reverse: \n" + sJ2.toString());
averse: Hello 2 Chocolate 10 reverse: 10 Chocolate 2 Hello
但是 descendingIterator 不适用于 List 和 ArrayList。 是否有任何解决方法或列表解释,为什么List中缺少descendingIterator?
问题类似于可以在java中以相反的顺序执行foreach循环吗?。但所有答案都推荐临时解决方案或第三方库。
可能有可能使用流流吗? (不幸的是,我的谷歌搜索仅给出了流缺乏标准反向Java 8流反向顺序)
正如我所提到的,我的问题与流有关,但是:
没有充分的理由解释为什么
List
不能有一个降序迭代器。每个 List
都需要支持 ListIterator
,可以使用 hasPrevious()
和 previous()
方法以相反顺序迭代。不过,这似乎是一个相当不常见的用例。
这里有一个小实用方法,可以将
Iterable
调整为 ListIterator
以相反顺序迭代:
static <T> Iterable<T> descendingIterable(List<? extends T> list) {
return () -> {
ListIterator<? extends T> li = list.listIterator(list.size());
return new Iterator<T>() {
public boolean hasNext() { return li.hasPrevious(); }
public T next() { return li.previous(); }
};
};
}
您可以使用它来实现您的示例:
List<String> list = Arrays.asList("Hello", "2", "Chocolate", "10");
StringJoiner sj = new StringJoiner(" ");
descendingIterable(list).iterator().forEachRemaining(sj::add);
System.out.println(sj);
10 Chocolate 2 Hello
或者您可以在增强的 for 循环中使用它:
for (String s : descendingIterable(list)) {
System.out.println(s);
}
10
Chocolate
2
Hello
我最初创建了一个
Iterator
而不是 Iterable
,但后者更有用,因为它可以在增强的 for 循环中使用。
还请注意,这里有一个小问题,即如果
List
可以同时修改,则存在竞争条件。这发生在此处的代码中:
list.listIterator(list.size())
如果可能,您必须在列表上使用外部同步,或者如果列表是
CopyOnWriteArrayList
,则必须先克隆它。有关后者的更多信息,请参阅此答案。
有关于为什么 List 中缺少 descendingIterator 的解释吗?
迭代列表的效率高度依赖于该列表的实现。
在设计接口时,您通常希望该接口适合其任何可能的实现。
Java 链表是一个双重链表,这意味着每个元素都链接到它之前和之后的元素,因此可以编写高效的升序和降序迭代器。
如果您使用singly链表,则降序迭代它的效率会非常低,您需要遍历整个列表,直到每次迭代的当前索引。
我怀疑正是由于这个原因,语言设计者决定从 List 接口中省略一个降序迭代器。
使用基于索引的方法可以相当轻松地实现高效的 ArrayList 降序迭代器,但在 LinkedList 上使用索引方法会比其首选实现慢得多。
降序迭代方法的一些示例:
for(int i=list.size() -1; i >= 0; i--){
System.out.println(list.get(i));
}
IntStream.range(0, list.size())
.map(i-> list.size() - 1 - i)
.mapToObj(list::get)
.forEach(System.out::println);
reversed()
方法可用于从列表中返回反转的 Iterator
。 在 list.reversed().iterator()
可用于 List
和其他 list.descendingIterator()
类型的情况下,LinkedList
可用于任何 Deque
类型。
List<Object> list = new ArrayList<Object>();
// ...
StringJoiner sJ1 = new StringJoiner(" ");
list.iterator().forEachRemaining(a -> sJ1.add(a.toString()));
System.out.println("averse: \n" + sJ1.toString());
StringJoiner sJ2 = new StringJoiner(" ");
list.reversed().iterator().forEachRemaining(a -> sJ2.add(a.toString()));
System.out.println("reverse: \n" + sJ2.toString());
虽然它没有解释 为什么 没有一致的
descendingIterator
,但 JEP 431:顺序集合 承认这是一个痛点,也是将 reversed()
方法引入 Java 的激励因素。
从头到尾迭代集合的元素是简单且一致的,但逆序迭代则不然。所有这些集合 [(表示具有定义的遇到顺序的元素序列)] 都可以使用
、增强的Iterator
循环、for
或stream()
向前迭代。每种情况下的反向迭代都是不同的。toArray()
提供用于反向迭代的NavigableSet
视图:descendingSet()
for (var e : navSet.descendingSet()) process(e);
则相反Deque
:Iterator
for (var it = deque.descendingIterator(); it.hasNext();) { var e = it.next(); process(e); }
这样做,但是使用List
:ListIterator
最后,for (var it = list.listIterator(list.size()); it.hasPrevious();) { var e = it.previous(); process(e); }
不支持反向迭代。以相反顺序处理LinkedHashSet
元素的唯一实用方法是将其元素复制到另一个集合中。LinkedHashSet