可以以相反的顺序为java中的每个循环做一个吗?

Posted

技术标签:

【中文标题】可以以相反的顺序为java中的每个循环做一个吗?【英文标题】:Can one do a for each loop in java in reverse order? 【发布时间】:2010-11-09 01:06:45 【问题描述】:

我需要使用 Java 以相反的顺序遍历列表。

那么它在哪里转发:

for(String string: stringList)
//...do something

有没有办法使用 for each 语法以相反的顺序迭代 stringList?

为了清楚起见:我知道如何以相反的顺序迭代列表,但想知道(出于好奇)如何以 for each 样式进行。

【问题讨论】:

“for-each”循环的要点是,你只需要对每个元素执行一次操作,顺序并不重要。 For-each 可以以完全随机的顺序处理元素,并且它仍然会按照它的设计目的进行。如果您需要以特定方式处理元素,我建议您手动进行。 Java 集合库。和语言真的没有任何关系。责怪乔什·布洛赫。 @muusbolla:但是作为一个 List 是一个 ordered 集合,它的顺序肯定会得到尊重,无论如何?因此 for-each 不会以随机顺序处理 List 的元素。 @muusbolla 这不是真的。也许在Set 派生集合的情况下。 foreach 保证按照集合的 iterator() 方法返回的迭代器的顺序进行迭代。 docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html 我也想要这个内置,在 Python 中我们可以写这个 for row in rows[::-1] 来做逆向迭代。为什么不用 Java? 【参考方案1】:

Collections.reverse 方法实际上返回一个新列表,其中原始列表的元素以相反的顺序复制到其中,因此在原始列表的大小方面具有 O(n) 性能。

作为一种更有效的解决方案,您可以编写一个装饰器,将 List 的反向视图呈现为 Iterable。装饰器返回的迭代器将使用装饰列表的 ListIterator 以相反的顺序遍历元素。

例如:

public class Reversed<T> implements Iterable<T> 
    private final List<T> original;

    public Reversed(List<T> original) 
        this.original = original;
    

    public Iterator<T> iterator() 
        final ListIterator<T> i = original.listIterator(original.size());

        return new Iterator<T>() 
            public boolean hasNext()  return i.hasPrevious(); 
            public T next()  return i.previous(); 
            public void remove()  i.remove(); 
        ;
    

    public static <T> Reversed<T> reversed(List<T> original) 
        return new Reversed<T>(original);
    

你会像这样使用它:

import static Reversed.reversed;

...

List<String> someStrings = getSomeStrings();
for (String s : reversed(someStrings)) 
    doSomethingWith(s);

【讨论】:

这基本上就是 Google 的 Iterables.reverse 所做的,是的 :) 我知道有一个“规则”,我们必须接受乔恩的回答 :) 但是.. 我想接受这个(即使它们本质上是相同的),因为它不需要我包括另一个 3rd 方库(尽管有些人可能会认为这个原因破坏了 OO 的主要优势之一 - 可重用性)。 小错误:在public void remove()中,不应该有return语句,应该只是:i.remove(); Collections.reverse() 不会返回反向副本,而是作用于作为参数传递给它的 List。不过,就像您使用迭代器的解决方案一样。真的很优雅。【参考方案2】:

对于列表,您可以使用Google Guava Library:

for (String item : Lists.reverse(stringList))

    // ...

注意Lists.reverse 反转整个集合,或做任何类似的事情 - 它只允许迭代和随机访问,以相反的顺序。这比先反转集合更有效。

要反转任意迭代,您必须阅读所有内容,然后向后“重放”它。

(如果您还没有使用它,我会彻底建议您看看Guava。这是很棒的东西。)

【讨论】:

我们的代码库大量使用了由 larvalabs (larvalabs.com/collections) 发布的 Commons Collections 的通用版本。查看 Apache Commons 的 SVN 存储库,很明显,发布 Java 5 版本的 Commons Collections 的大部分工作已经完成,只是还没有发布。 我喜欢。如果它不是那么有用,我会称之为插头。 我想知道为什么 Jakarta 从不费心更新 Apache Commons。 他们已经更新了,这就是我要说的。他们只是没有发布它。 Iterables.reverse 已被弃用,请改用 Lists.reverse 或 ImmutableList.reverse。【参考方案3】:

List(与 Set 不同)是一个有序集合,对其进行迭代确实会按合同保留顺序。我本来希望 Stack 以相反的顺序迭代,但不幸的是它没有。所以我能想到的最简单的解决方案是这样的:

for (int i = stack.size() - 1; i >= 0; i--) 
    System.out.println(stack.get(i));

我意识到这不是“for each”循环解决方案。我宁愿使用 for 循环,也不愿引入像 Google Collections 这样的新库。

Collections.reverse() 也可以完成这项工作,但它会更新列表,而不是按相反的顺序返回副本。

【讨论】:

这种方法对于基于数组的列表(例如 ArrayList)可能很好,但对于链接列表来说不是最佳的,因为每个 get 都必须从头到尾遍历列表(或者可能从头到尾遍历)开始)对于每个获取。最好使用像 Nat 的解决方案中更智能的迭代器(对于 List 的所有实现都是最佳的)。 此外,它偏离了 OP 中明确要求 for each 语法的请求 更好的可读性:for (int i = stack.size(); i-- &gt;0;) 【参考方案4】:

这会弄乱原始列表,并且还需要在循环之外调用。 此外,您不希望每次循环时都执行反向操作 - 如果应用了 Iterables.reverse ideas 之一,那会是真的吗?

Collections.reverse(stringList);

for(String string: stringList)
//...do something

【讨论】:

【参考方案5】:

AFAIK 标准库中没有标准的“reverse_iterator”之类的东西支持 for-each 语法,这已经是他们在语言后期引入的语法糖。

您可以执行类似 for(Item element: myList.clone().reverse()) 的操作并支付相关价格。

这似乎也与没有为您提供进行昂贵操作的便捷方法的明显现象相当一致 - 因为根据定义,列表可能具有 O(N) 随机访问复杂度(您可以使用单链接实现接口),反向迭代最终可能是 O(N^2)。当然,如果你有一个 ArrayList,你就不用付出这个代价。

【讨论】:

你可以向后运行一个 ListIterator,它可以被一个 Iterator 包裹起来。 @Tom:好点子。但是,使用迭代器,您仍然在执行烦人的旧式 for 循环,并且您可能仍然需要付出代价才能到达最后一个元素......不过,我在答案中添加了限定符,谢谢。跨度> 双端队列有一个反向迭代器。【参考方案6】:

截至the comment:您应该可以使用Apache Commons ReverseListIterator

Iterable<String> reverse 
    = new IteratorIterable(new ReverseListIterator(stringList));

for(String string: reverse )
    //...do something

作为@rogerdpack said,您需要将ReverseListIterator 包装为Iterable

作为recommended by Roland Nordborg-Løvstad in the comments,您可以在当前 Java 中使用 Lambda 进行简化

Iterable<String> reverse = () -> new ReverseListIterator<>(stringList)

【讨论】:

在以后的java版本中,可以使用这个简写:Iterable reverse = () -> new ReverseListIterator(stringList)【参考方案7】:

这可能是一个选项。希望有更好的方法从最后一个元素开始,而不是从 while 循环到结束。

public static void main(String[] args)         
    List<String> a = new ArrayList<String>();
    a.add("1");a.add("2");a.add("3");a.add("4");a.add("5");

    ListIterator<String> aIter=a.listIterator();        
    while(aIter.hasNext()) aIter.next();

    for (;aIter.hasPrevious();)
    
        String aVal = aIter.previous();
        System.out.println(aVal);           
    

【讨论】:

【参考方案8】:

一个工作:

Collections.reverse(stringList).forEach(str -> ...);

或者guava:

Lists.reverse(stringList).forEach(str -> ...);

【讨论】:

【参考方案9】:

如果没有编写一些自定义代码,它会给你一个枚举器,它将为你反转元素。

您应该能够在 Java 中通过创建 Iterable 的自定义实现来实现,它将以相反的顺序返回元素。

然后,您将实例化包装器(或调用方法,what-have-you),该包装器将返回 Iterable 实现,该实现在 for each 循环中反转元素。

【讨论】:

【参考方案10】:

如果您想使用 for each 开箱即用的语法并以相反的顺序进行,则需要反转您的集合。

【讨论】:

【参考方案11】:

以上所有答案都只满足要求,要么包装另一种方法,要么在外面调用一些外来代码;

这是从Thinking in Java 4th edition,第 11.13.1 章 AdapterMethodIdiom 复制的解决方案;

代码如下:

// The "Adapter Method" idiom allows you to use foreach
// with additional kinds of Iterables.
package holding;
import java.util.*;

@SuppressWarnings("serial")
class ReversibleArrayList<T> extends ArrayList<T> 
  public ReversibleArrayList(Collection<T> c)  super(c); 
  public Iterable<T> reversed() 
    return new Iterable<T>() 
      public Iterator<T> iterator() 
        return new Iterator<T>() 
          int current = size() - 1; //why this.size() or super.size() wrong?
          public boolean hasNext()  return current > -1; 
          public T next()  return get(current--); 
          public void remove()  // Not implemented
            throw new UnsupportedOperationException();
          
        ;
      
    ;
  
   

public class AdapterMethodIdiom 
  public static void main(String[] args) 
    ReversibleArrayList<String> ral =
      new ReversibleArrayList<String>(
        Arrays.asList("To be or not to be".split(" ")));
    // Grabs the ordinary iterator via iterator():
    for(String s : ral)
      System.out.print(s + " ");
    System.out.println();
    // Hand it the Iterable of your choice
    for(String s : ral.reversed())
      System.out.print(s + " ");
  
 /* Output:
To be or not to be
be to not or be To
*///:~

【讨论】:

为什么int current = size() - 1 是对的?为什么不是int current = this.size() - 1int current = super.size() - 1【参考方案12】:

您可以使用Collections 类来反转列表然后循环。

【讨论】:

【参考方案13】:

这个问题的答案肯定是迟到的。一种可能性是在 for 循环中使用 ListIterator。它不像冒号语法那样干净,但它有效。

List<String> exampleList = new ArrayList<>();
exampleList.add("One");
exampleList.add("Two");
exampleList.add("Three");

//Forward iteration
for (String currentString : exampleList) 
    System.out.println(currentString); 


//Reverse iteration
for (ListIterator<String> itr = exampleList.listIterator(exampleList.size()); itr.hasPrevious(); /*no-op*/ ) 
    String currentString = itr.previous();
    System.out.println(currentString); 

ListIterator 语法的功劳归于"Ways to iterate over a list in Java"

【讨论】:

【参考方案14】:

例如

Integer[][] a = 
                 1, 2, 3 , 
                 4, 5, 6 , 
                 9, 8, 9 , 
                ;

List<List<Integer>> arr = Arrays.stream(a)
                .map(Arrays::asList)
                .collect(Collectors.toList());

现在反转它。

Collections.reverse(arr);
System.out.println("Reverse Array" + arr);
for (List<Integer> obj : arr) 
    // TODO

【讨论】:

以上是关于可以以相反的顺序为java中的每个循环做一个吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何以相反的顺序快速迭代for循环?

我可以将字符串存储在字符数组中但顺序相反,例如 0(index) 指向字符串中的最后一个字符吗?

顺时针打印矩阵

[3D数学]矩阵 2017.8.16

是否可以以相反的顺序使System.out.print排序变量?

Java中的链表有快速的concat方法吗?