我正在迁移一段代码以使用泛型。这样做的一个论点是,for 循环比跟踪索引或使用显式迭代器要干净得多。
在大约一半的情况下,现在使用索引以相反的顺序迭代列表(一个 ArrayList)。
有人可以建议一种更清洁的方法(因为我在使用集合时不喜欢 indexed for loop
),尽管它确实有效?
for (int i = nodes.size() - 1; i >= 0; i--) {
final Node each = (Node) nodes.get(i);
...
}
注意:我不能在 JDK 之外添加任何新的依赖项。
for (int i = nodes.size(); --i >= 0;)
尝试这个:
// Substitute appropriate type.
ArrayList<...> a = new ArrayList<...>();
// Add elements to list.
// Generate an iterator. Start just after the last element.
ListIterator li = a.listIterator(a.size());
// Iterate in reverse.
while(li.hasPrevious()) {
System.out.println(li.previous());
}
Guava 提供 Lists#reverse(List)
和 ImmutableList#reverse()
。与 Guava 的大多数情况一样,如果参数是 ImmutableList
,则前者委托给后者,因此您可以在所有情况下使用前者。这些不会创建列表的新副本,而只是“反转视图”。
例子
List reversed = ImmutableList.copyOf(myList).reverse();
我认为不可能使用 for 循环语法。我唯一能建议的是做类似的事情:
Collections.reverse(list);
for (Object o : list) {
...
}
...但我不会说这是“更清洁”,因为它的效率会降低。
选项 1:您是否考虑过使用 Collections#reverse() 反转列表,然后使用 foreach?
当然,您可能还想重构代码以使列表正确排序,这样您就不必反转它,这会使用额外的空间/时间。
编辑:
选项 2:或者,您可以使用 Deque 代替 ArrayList 吗?它将允许您向前和向后迭代
编辑:
选项 3:正如其他人所建议的,您可以编写一个反向遍历列表的迭代器,这是一个示例:
import java.util.Iterator;
import java.util.List;
public class ReverseIterator<T> implements Iterator<T>, Iterable<T> {
private final List<T> list;
private int position;
public ReverseIterator(List<T> list) {
this.list = list;
this.position = list.size() - 1;
}
@Override
public Iterator<T> iterator() {
return this;
}
@Override
public boolean hasNext() {
return position >= 0;
}
@Override
public T next() {
return list.get(position--);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
for (String s : new ReverseIterator<String>(list)) {
System.out.println(s);
}
descendingIterator()
。
for each
表达式是最惯用的解决方案。很高兴意识到如果您的 List 以向后迭代的方式实现 Iterable ,这是可能的。我将使用这种方法并使用 Apache Commons Collections 中的 ReverseListIterator 类。
您可以使用具体类 LinkedList
而不是通用接口 List
。然后你有一个 descendingIterator
用于反向迭代。
LinkedList<String > linkedList;
for( Iterator<String > it = linkedList.descendingIterator(); it.hasNext(); ) {
String text = it.next();
}
不知道为什么没有 descendingIterator
和 ArrayList
...
这是一个老问题,但它缺乏对 java8 友好的答案。在 Streaming API 的帮助下,以下是一些反向迭代列表的方法:
List<Integer> list = new ArrayList<Integer>(Arrays.asList(1, 3, 3, 7, 5));
list.stream().forEach(System.out::println); // 1 3 3 7 5
int size = list.size();
ListIterator<Integer> it = list.listIterator(size);
Stream.generate(it::previous).limit(size)
.forEach(System.out::println); // 5 7 3 3 1
ListIterator<Integer> it2 = list.listIterator(size);
Stream.iterate(it2.previous(), i -> it2.previous()).limit(size)
.forEach(System.out::println); // 5 7 3 3 1
// If list is RandomAccess (i.e. an ArrayList)
IntStream.range(0, size).map(i -> size - i - 1).map(list::get)
.forEach(System.out::println); // 5 7 3 3 1
// If list is RandomAccess (i.e. an ArrayList), less efficient due to sorting
IntStream.range(0, size).boxed().sorted(Comparator.reverseOrder())
.map(list::get).forEach(System.out::println); // 5 7 3 3 1
这是 ReverseIterable
的(未经测试的)实现。调用 iterator()
时,它会创建并返回私有 ReverseIterator
实现,该实现只是将对 hasNext()
的调用映射到 hasPrevious()
,对 next()
的调用映射到 previous()
。这意味着您可以反向遍历 ArrayList
,如下所示:
ArrayList<String> l = ...
for (String s : new ReverseIterable(l)) {
System.err.println(s);
}
类定义
public class ReverseIterable<T> implements Iterable<T> {
private static class ReverseIterator<T> implements Iterator {
private final ListIterator<T> it;
public boolean hasNext() {
return it.hasPrevious();
}
public T next() {
return it.previous();
}
public void remove() {
it.remove();
}
}
private final ArrayList<T> l;
public ReverseIterable(ArrayList<T> l) {
this.l = l;
}
public Iterator<T> iterator() {
return new ReverseIterator(l.listIterator(l.size()));
}
}
ReverseIterator
缺少必要的构造函数,代码应该使用 List
而不是 ArrayList
。
如果列表相当小以至于性能不是一个真正的问题,则可以使用 Google Guava
中 Lists
类的 reverse
方法。产生漂亮的 for-each
代码,并且原始列表保持不变。此外,反向列表由原始列表支持,因此对原始列表的任何更改都将反映在反向列表中。
import com.google.common.collect.Lists;
[...]
final List<String> myList = Lists.newArrayList("one", "two", "three");
final List<String> myReverseList = Lists.reverse(myList);
System.out.println(myList);
System.out.println(myReverseList);
myList.add("four");
System.out.println(myList);
System.out.println(myReverseList);
产生以下结果:
[one, two, three]
[three, two, one]
[one, two, three, four]
[four, three, two, one]
这意味着 myList 的反向迭代可以写成:
for (final String someString : Lists.reverse(myList)) {
//do something
}
您可以使用 Apache Commons-Collections 中的 ReverseListIterator
:
ReverseListIterator
链接在这一点上确实有效。 (2020 年 12 月更新)
非常简单的例子:
List<String> list = new ArrayList<String>();
list.add("ravi");
list.add("kant");
list.add("soni");
// Iterate to disply : result will be as --- ravi kant soni
for (String name : list) {
...
}
//Now call this method
Collections.reverse(list);
// iterate and print index wise : result will be as --- soni kant ravi
for (String name : list) {
...
}
创建自定义 reverseIterable
。
拥有如下所示的代码:
List<Item> items;
...
for (Item item : In.reverse(items))
{
...
}
将此代码放入名为“In.java”的文件中:
import java.util.*;
public enum In {;
public static final <T> Iterable<T> reverse(final List<T> list) {
return new ListReverseIterable<T>(list);
}
class ListReverseIterable<T> implements Iterable<T> {
private final List<T> mList;
public ListReverseIterable(final List<T> list) {
mList = list;
}
public Iterator<T> iterator() {
return new Iterator<T>() {
final ListIterator<T> it = mList.listIterator(mList.size());
public boolean hasNext() {
return it.hasPrevious();
}
public T next() {
return it.previous();
}
public void remove() {
it.remove();
}
};
}
}
}
listIterator
字段需要在 Iterator
实现中,而不是在 Iterable
实现中。
name()
方法、一个 ordinal()
方法和一个 static valueOf()
方法。
正如至少两次建议的那样,您可以将 descendingIterator
与 Deque
一起使用,尤其是与 LinkedList
一起使用。如果你想使用 for-each 循环(即有一个 Iterable
),你可以像这样构造和使用一个包装器:
import java.util.*;
public class Main {
public static class ReverseIterating<T> implements Iterable<T> {
private final LinkedList<T> list;
public ReverseIterating(LinkedList<T> list) {
this.list = list;
}
@Override
public Iterator<T> iterator() {
return list.descendingIterator();
}
}
public static void main(String... args) {
LinkedList<String> list = new LinkedList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
for (String s : new ReverseIterating<String>(list)) {
System.out.println(s);
}
}
}
Valid for Java 9+
List<String> strList = List.of("a", "b", "c", "d", "e");
IntStream.iterate(strList.size() - 1, i -> i >= 0, i -> --i)
.mapToObj(strList::get)
.forEach(System.out::println);
原因:“不知道为什么ArrayList没有descendingIterator...”
由于数组列表不保持列表与数据添加到列表中的顺序相同。所以,永远不要使用 Arraylist 。
链表将按照添加到列表的相同顺序保持数据。
所以,在上面的例子中,我使用了 ArrayList() 来让用户改变他们的想法,让他们从他们身边锻炼一些东西。
而不是这个
List<String> list = new ArrayList<String>();
利用:
List<String> list = new LinkedList<String>();
list.add("ravi");
list.add("kant");
list.add("soni");
// Iterate to disply : result will be as --- ravi kant soni
for (String name : list) {
...
}
//Now call this method
Collections.reverse(list);
// iterate and print index wise : result will be as --- soni kant ravi
for (String name : list) {
...
}
listIterator
调用的索引。ListIterator
的Iterator
,但这对于一个循环可能不值得。for (Node each : new ListReverse<Node>(nodes)) { }