Người ta có thể làm một cho mỗi vòng lặp trong java theo thứ tự ngược lại không?


148

Tôi cần chạy qua một Danh sách theo thứ tự ngược lại bằng Java.

Vì vậy, nơi này làm điều đó chuyển tiếp:

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

Có cách nào để lặp chuỗi StringList theo thứ tự ngược lại bằng cách sử dụng cho mỗi cú pháp không?

Để rõ ràng: Tôi biết cách lặp lại một danh sách theo thứ tự ngược lại nhưng muốn biết (vì sự tò mò) làm thế nào để làm điều đó theo từng phong cách.


5
Điểm của vòng lặp "cho mỗi" là bạn chỉ cần thực hiện một thao tác trên mỗi phần tử và thứ tự không quan trọng. Mỗi người có thể xử lý các phần tử theo thứ tự hoàn toàn ngẫu nhiên, và nó vẫn sẽ làm những gì nó được thiết kế cho. Nếu bạn cần xử lý các yếu tố theo một cách cụ thể, tôi khuyên bạn nên thực hiện thủ công.
muusbolla

Thư viện bộ sưu tập Java. Không thực sự có bất cứ điều gì để làm với ngôn ngữ. Đổ lỗi cho Josh Bloch.
Tom Hawtin - tackline

7
@muusbolla: Nhưng vì Danh sách là một bộ sưu tập được đặt hàng , chắc chắn đơn hàng của nó sẽ được vinh danh, bất kể? Do đó, mỗi người sẽ không xử lý các thành phần của Danh sách theo thứ tự ngẫu nhiên.
Lee Kowalkowski

5
@muusbolla điều đó không đúng. Có thể trong trường hợp Setbộ sưu tập dẫn xuất. foreachđảm bảo việc lặp theo thứ tự của trình lặp được trả về từ iterator()phương thức của bộ sưu tập. docs.oracle.com/javase/1.5.0/docs/guide/lingu/foreach.html
robert

Câu trả lời:


151

Phương thức Collections.reverse thực sự trả về một danh sách mới với các phần tử của danh sách gốc được sao chép vào đó theo thứ tự ngược lại, do đó, điều này có hiệu suất O (n) liên quan đến kích thước của danh sách gốc.

Là một giải pháp hiệu quả hơn, bạn có thể viết một trình trang trí trình bày một cái nhìn đảo ngược của Danh sách dưới dạng Iterable. Trình lặp được trả về bởi trình trang trí của bạn sẽ sử dụng ListIterator của danh sách được trang trí để đi qua các phần tử theo thứ tự ngược lại.

Ví dụ:

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);
    }
}

Và bạn sẽ sử dụng nó như sau:

import static Reversed.reversed;

...

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

22
Về cơ bản đó là những gì Iterables.reverse của Google làm, vâng :)
Jon Skeet

10
Tôi biết có một 'quy tắc' mà chúng ta phải chấp nhận câu trả lời của Jon :) nhưng .. Tôi muốn chấp nhận điều này (mặc dù về cơ bản là giống nhau) vì nó không yêu cầu tôi đưa vào thư viện của bên thứ 3 khác (mặc dù một số người có thể lập luận rằng lý do đó phá vỡ một trong những lợi thế chính của OO - tái sử dụng).
Ron Tuffin

Lỗi nhỏ: Trong void void remove (), không nên có câu lệnh return, nó chỉ là: i.remove ();
Jesper

10
Bộ sưu tập.reverse () KHÔNG trả về một bản sao bị đảo ngược mà hoạt động trên Danh sách được truyền cho nó dưới dạng tham số. Giống như giải pháp của bạn với iterator, mặc dù. Thực sự thanh lịch.
er4z0r

96

Để biết danh sách, bạn có thể sử dụng Thư viện Guava của Google :

for (String item : Lists.reverse(stringList))
{
    // ...
}

Lưu ý rằng không đảo ngược toàn bộ bộ sưu tập hoặc làm bất cứ điều gì giống như nó - nó chỉ cho phép lặp lại và truy cập ngẫu nhiên, theo thứ tự ngược lại. Điều này là hiệu quả hơn so với đảo ngược bộ sưu tập đầu tiên.Lists.reverse

Để đảo ngược một lần lặp tùy ý, bạn phải đọc tất cả và sau đó "phát lại" nó ngược lại.

(Nếu bạn chưa sử dụng nó, tôi hoàn toàn khuyên bạn nên xem ổi . Đó là thứ tuyệt vời.)


1
Codebase của chúng tôi sử dụng rất nhiều phiên bản Bộ sưu tập Commons được phát hành bởi larvalabs ( larvalabs.com/collections ). Nhìn qua repo SVN cho Apache Commons, rõ ràng phần lớn công việc phát hành phiên bản java 5 của Bộ sưu tập Commons đã hoàn tất, họ vẫn chưa phát hành nó.
skaffman

Tôi thích nó. Nếu nó không hữu ích, tôi sẽ gọi đó là phích cắm.
geowa4

Tôi tự hỏi tại sao Jakarta không bao giờ bận tâm cập nhật Apache Commons.
Uri

3
Họ đã cập nhật nó, đó là những gì tôi đang nói. Họ chưa phát hành nó.
skaffman

23
Iterables.reverse đã không được chấp nhận, thay vào đó, hãy sử dụng Lists.reverse hoặc ImmutableList.reverse.
Hội trường Garrett

39

Danh sách (không giống như Bộ) là một bộ sưu tập được đặt hàng và lặp đi lặp lại trên đó sẽ bảo toàn thứ tự theo hợp đồng. Tôi đã mong đợi một Stack để lặp theo thứ tự ngược lại nhưng thật không may, nó không. Vì vậy, giải pháp đơn giản nhất tôi có thể nghĩ đến là:

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

Tôi nhận ra rằng đây không phải là giải pháp vòng lặp "cho mỗi". Tôi thà sử dụng vòng lặp for hơn là giới thiệu một thư viện mới như Bộ sưu tập Google.

Bộ sưu tập.reverse () cũng thực hiện công việc nhưng nó cập nhật danh sách thay vì trả lại một bản sao theo thứ tự ngược lại.


3
Cách tiếp cận này có thể tốt cho các danh sách dựa trên mảng (như ArrayList) nhưng nó sẽ là tối ưu phụ cho Danh sách được liên kết vì mỗi get sẽ phải duyệt qua danh sách từ đầu đến cuối (hoặc có thể từ đầu đến đầu) cho mỗi get. Tốt hơn là sử dụng một trình vòng lặp thông minh hơn như trong giải pháp của Nat (tối ưu cho tất cả các triển khai Danh sách).
Chris

1
Hơn nữa, nó đi lạc khỏi yêu cầu trong OP yêu cầu for eachcú pháp rõ ràng
Paul W

8

Điều này sẽ gây rối với danh sách ban đầu và cũng cần được gọi bên ngoài vòng lặp. Ngoài ra, bạn không muốn thực hiện đảo ngược mỗi khi bạn lặp - điều đó có đúng không nếu một trong những điều đó Iterables.reverse ideasđược áp dụng?

Collections.reverse(stringList);

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

5

AFAIK không có một loại "Reverse_iterator" tiêu chuẩn trong thư viện tiêu chuẩn hỗ trợ cú pháp cho mỗi cú pháp vốn là một cú pháp cú pháp mà họ đưa vào ngôn ngữ.

Bạn có thể làm một cái gì đó như cho (Phần tử mục: myList.clone (). Reverse ()) và trả giá liên quan.

Điều này cũng có vẻ khá phù hợp với hiện tượng rõ ràng là không cung cấp cho bạn các cách thuận tiện để thực hiện các thao tác đắt tiền - vì một danh sách, theo định nghĩa, có thể có độ phức tạp truy cập ngẫu nhiên O (N) (bạn có thể thực hiện giao diện với một liên kết đơn), ngược lại lặp đi lặp lại có thể là O (N ^ 2). Tất nhiên, nếu bạn có ArrayList, bạn không phải trả giá đó.


Bạn có thể chạy ListIterator ngược, có thể được gói trong Iterator.
Tom Hawtin - tackline

@Tom: Điểm tốt. Tuy nhiên, với iterator, bạn vẫn đang thực hiện kiểu vòng lặp phiền phức cho các vòng lặp và bạn vẫn có thể trả chi phí để đi đến phần tử cuối cùng để bắt đầu ... Tuy nhiên, tôi đã thêm vào vòng loại trong câu trả lời của mình.
Uri

Deque có một vòng lặp ngược.
Michael Munsey

2

Đây có thể là một lựa chọn. Hy vọng có một cách tốt hơn để bắt đầu từ yếu tố cuối cùng hơn là vòng lặp while đến cuối.

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);           
    }
}


1

Không phải không viết một số mã tùy chỉnh sẽ cung cấp cho bạn một điều tra viên sẽ đảo ngược các yếu tố cho bạn.

Bạn sẽ có thể làm điều đó trong Java bằng cách tạo một triển khai tùy chỉnh Iterable sẽ trả về các phần tử theo thứ tự ngược lại.

Sau đó, bạn sẽ khởi tạo trình bao bọc (hoặc gọi phương thức, what-have-you) sẽ trả về việc thực hiện Iterable có thể đảo ngược phần tử trong mỗi vòng lặp.



1

Bạn cần đảo ngược bộ sưu tập của mình nếu bạn muốn sử dụng cho từng cú pháp ra khỏi hộp và đi theo thứ tự ngược lại.


1

Tất cả các câu trả lời ở trên chỉ đáp ứng yêu cầu, bằng cách gói một phương thức khác hoặc gọi một số mã nước ngoài bên ngoài;

Đây là giải pháp được sao chép từ Thinking in Java phiên bản thứ 4 , chương 11.13.1 AdapterMethodIdiom ;

Đây là mã:

// 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
*///:~

tại sao lại int current = size() - 1đúng tại sao không int current = this.size() - 1hoặcint current = super.size() - 1
qiwen li

1

Một công việc xung quanh:

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

Hoặc với ổi :

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

0

Chắc chắn là một câu trả lời muộn cho câu hỏi này. Một khả năng là sử dụng ListIterator trong một vòng lặp for. Nó không sạch như cú pháp dấu hai chấm, nhưng nó hoạt động.

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); 
}

Tín dụng cho cú pháp ListIterator chuyển đến "Cách lặp lại danh sách trong Java"

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.