หนึ่งสามารถทำสำหรับแต่ละวงใน java ในลำดับย้อนกลับได้หรือไม่


148

ฉันต้องการเรียกใช้ผ่านรายการในลำดับย้อนกลับโดยใช้ Java

ดังนั้นสิ่งนี้จะส่งต่อไปที่ใด:

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

มีวิธีการซ้ำ stringList ในลำดับย้อนกลับโดยใช้สำหรับแต่ละไวยากรณ์หรือไม่

เพื่อความชัดเจน: ฉันรู้วิธีย้ำรายการในลำดับย้อนกลับ แต่อยากรู้ (เพื่อประโยชน์ของความอยากรู้) วิธีการทำในแต่ละสไตล์


5
จุดของลูป "for-each" คือการที่คุณเพียงแค่ต้องทำการดำเนินการกับแต่ละองค์ประกอบและลำดับไม่สำคัญ สำหรับแต่ละคนสามารถประมวลผลองค์ประกอบตามลำดับอย่างสมบูรณ์และมันจะยังคงทำสิ่งที่มันถูกออกแบบมาสำหรับ หากคุณต้องการประมวลผลองค์ประกอบด้วยวิธีเฉพาะฉันขอแนะนำให้ทำด้วยตนเอง
muusbolla

ห้องสมุดคอลเลกชัน Java ไม่ใช่เรื่องเกี่ยวกับภาษา ตำหนิ Josh Bloch
Tom Hawtin - tackline

7
@muusbolla: แต่เป็นรายการเป็นคอลเลกชันที่สั่งซื้อแน่นอนมันจะได้รับเกียรติโดยไม่คำนึงถึง? ดังนั้นสำหรับแต่ละคนจะไม่ประมวลผลองค์ประกอบของรายการตามลำดับแบบสุ่ม
Lee Kowalkowski

5
@muusbolla ที่ไม่เป็นความจริง บางทีในกรณีของSetคอลเลกชันที่ได้รับ foreachรับประกันการวนซ้ำในลำดับของตัววนซ้ำที่ส่งคืนจากiterator()วิธีการรวบรวม docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
robert

คำตอบ:


151

วิธี Collections.reverse จะส่งคืนรายการใหม่ที่มีองค์ประกอบของรายการต้นฉบับที่คัดลอกมาไว้ในลำดับย้อนกลับดังนั้นจึงมีประสิทธิภาพ O (n) เกี่ยวกับขนาดของรายการต้นฉบับ

ในฐานะที่เป็นโซลูชั่นที่มีประสิทธิภาพมากขึ้นคุณสามารถเขียนมัณฑนากรที่นำเสนอมุมมองที่ย้อนกลับของรายการเป็น 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);
}

22
นั่นเป็นสิ่งที่ Iterables.reverse ของ Google ไม่ใช่ :)
จอนสกีต

10
ฉันรู้ว่ามี 'กฎ' ที่เราต้องยอมรับคำตอบของ Jon :) แต่ .. ฉันต้องการยอมรับอันนี้ (แม้ว่าพวกเขาจะเหมือนกัน) เพราะมันไม่ต้องการให้ฉันรวมไลบรารี่ของบุคคลที่สามอีกด้วย (แม้ว่า บางคนอาจโต้แย้งว่าเหตุผลนั้นทำลายข้อได้เปรียบสำคัญอย่างหนึ่งของ OO - reusability)
Ron Tuffin

ข้อผิดพลาดเล็กน้อย: ในโมฆะ public remove () ไม่ควรมีคำสั่ง return มันควรจะเป็นแค่: i.remove ();
Jesper

10
Collections.reverse () ไม่ส่งคืนสำเนาที่กลับรายการ แต่ทำหน้าที่ในรายการที่ส่งให้เป็นพารามิเตอร์ เช่นเดียวกับวิธีแก้ปัญหาของคุณด้วยตัววนซ้ำ สง่างามจริงๆ
er4z0r

96

สำหรับรายการคุณสามารถใช้ห้องสมุด Google Guava :

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

โปรดทราบว่าจะไม่ย้อนกลับการรวบรวมทั้งหมดหรือทำอะไรเช่นนั้น - เพียงแค่อนุญาตให้วนซ้ำและเข้าถึงแบบสุ่มในลำดับย้อนกลับ สิ่งนี้มีประสิทธิภาพมากกว่าการย้อนกลับการรวบรวมก่อนLists.reverse

หากต้องการย้อนกลับการทำซ้ำโดยพลการคุณต้องอ่านทั้งหมดแล้ว "เล่นซ้ำ" ซ้ำไปด้านหลัง

(ถ้าคุณไม่ได้ใช้มันฉันอย่างละเอียดขอแนะนำให้คุณมีลักษณะที่เป็นฝรั่ง . มันเป็นสิ่งที่ดี.)


1
codebase ของเราใช้ประโยชน์อย่างหนักจาก Commons Collections รุ่นที่เผยแพร่โดย larvalabs ( larvalabs.com/collections ) เมื่อมองผ่าน SVN repo สำหรับ Apache Commons เป็นที่ชัดเจนว่างานส่วนใหญ่ในการเผยแพร่ Commons Collections รุ่น java 5 เสร็จสิ้นแล้วพวกเขาเพิ่งยังไม่ได้เปิดตัว
skaffman

ฉันชอบมัน. ถ้ามันไม่มีประโยชน์ฉันจะเรียกมันว่าปลั๊ก
geowa4

ฉันสงสัยว่าทำไมจาการ์ตาไม่เคยใส่ใจที่จะอัปเดต Apache Commons
Uri

3
พวกเขาอัปเดตแล้วนั่นคือสิ่งที่ฉันกำลังพูด พวกเขายังไม่ได้เปิดตัว
skaffman

23
Iterables.reverse เลิกใช้แล้วใช้ Lists.reverse หรือ ImmutableList.reverse แทน
Garrett Hall

39

รายการ (ซึ่งแตกต่างจากชุด) เป็นคอลเลกชันที่ได้รับคำสั่งและวนซ้ำมันจะรักษาลำดับโดยสัญญา ฉันคาดว่า Stack จะวนซ้ำในลำดับย้อนกลับ แต่โชคไม่ดี ดังนั้นทางออกที่ง่ายที่สุดที่ฉันคิดได้คือ:

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

ฉันรู้ว่านี่ไม่ใช่การวนลูป "สำหรับแต่ละ" ฉันควรใช้วงวนแทนการแนะนำห้องสมุดใหม่เช่น Google Collections

Collections.reverse () ทำหน้าที่นี้เช่นกัน แต่จะอัพเดตรายการเมื่อเทียบกับการส่งคืนสำเนาในลำดับย้อนกลับ


3
วิธีการนี้อาจใช้ได้สำหรับรายการที่มีอาเรย์ (เช่น ArrayList) แต่จะดีที่สุดสำหรับลิสต์ที่เชื่อมโยงเนื่องจากการรับแต่ละครั้งจะต้องสำรวจรายการตั้งแต่ต้นจนจบ มันจะดีกว่าที่จะใช้ iterator ที่ชาญฉลาดเช่นเดียวกับในโซลูชันของ Nat (ดีที่สุดสำหรับการใช้งานทั้งหมดของรายการ)
Chris

1
นอกจากนี้ยังอยู่ไกลจากคำขอใน OP ซึ่งขอfor eachไวยากรณ์อย่างชัดเจน
Paul W

8

สิ่งนี้จะยุ่งกับรายการต้นฉบับและจำเป็นต้องเรียกใช้นอกลูป นอกจากนี้คุณไม่ต้องการที่จะดำเนินการย้อนกลับทุกครั้งที่คุณห่วง - ที่จะเป็นจริงได้หากIterables.reverse ideasถูกนำมาใช้?

Collections.reverse(stringList);

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

5

AFAIK ไม่มีการเรียงลำดับสิ่ง "reverse_iterator" มาตรฐานในไลบรารีมาตรฐานที่รองรับไวยากรณ์สำหรับแต่ละคำซึ่งมีน้ำตาลประโยคที่พวกเขานำมาสู่ภาษา

คุณสามารถทำสิ่งที่ต้องการ (องค์ประกอบรายการ: myList.clone (). ย้อนกลับ ()) และชำระราคาที่เกี่ยวข้อง

สิ่งนี้ดูเหมือนว่าค่อนข้างสอดคล้องกับปรากฏการณ์ที่เห็นได้ชัดว่าไม่ให้วิธีที่สะดวกในการดำเนินการที่มีราคาแพง - เนื่องจากรายชื่อตามคำจำกัดความอาจมีความซับซ้อนในการเข้าถึงแบบสุ่ม O (N) (คุณสามารถใช้อินเทอร์เฟซเดียว การทำซ้ำอาจเป็น O (N ^ 2) แน่นอนถ้าคุณมี ArrayList คุณไม่ต้องจ่ายราคานั้น


คุณสามารถเรียกใช้ ListIterator ไปข้างหลังซึ่งสามารถรวมไว้ใน Iterator
Tom Hawtin - tackline

@Tom: จุดดี อย่างไรก็ตามด้วยตัววนซ้ำคุณยังคงทำรูปแบบเก่า ๆ ที่น่ารำคาญสำหรับลูปและคุณอาจยังต้องจ่ายค่าใช้จ่ายเพื่อไปยังองค์ประกอบสุดท้ายเพื่อเริ่มต้นด้วย ... ฉันได้เพิ่มคุณสมบัติในคำตอบของฉันแล้วขอบคุณ
Uri

Deque มีตัววนซ้ำย้อนกลับ
Michael Munsey

2

นี่อาจเป็นตัวเลือก หวังว่าจะมีวิธีที่ดีกว่าในการเริ่มต้นจากองค์ประกอบสุดท้ายกว่าไปจนถึงขณะที่วนซ้ำไปจนจบ

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

ไม่ได้โดยไม่ต้องเขียนรหัสที่กำหนดเองซึ่งจะทำให้คุณมีตัวแจงนับซึ่งจะกลับองค์ประกอบสำหรับคุณ

คุณควรจะสามารถทำได้ใน Java โดยการสร้างการใช้งานแบบกำหนดเองของ Iterable ซึ่งจะส่งคืนองค์ประกอบในลำดับย้อนกลับ

จากนั้นคุณจะสร้างอินสแตนซ์ของ wrapper (หรือเรียกเมธอด what-have-you) ซึ่งจะคืนค่าการใช้ Iterable ซึ่งจะย้อนกลับองค์ประกอบในแต่ละลูป



1

คุณจะต้องย้อนกลับคอลเลกชันของคุณถ้าคุณต้องการที่จะใช้สำหรับแต่ละไวยากรณ์ออกจากกล่องและไปในลำดับที่กลับกัน


1

คำตอบทั้งหมดข้างต้นเป็นไปตามข้อกำหนดเท่านั้นไม่ว่าจะด้วยวิธีอื่นหรือโทรหารหัสต่างประเทศนอก

นี่คือโซลูชันที่คัดลอกมาจาก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() - 1หรือint current = super.size() - 1
Qiwen li


0

แน่นอนคำตอบสำหรับคำถามนี้ ความเป็นไปได้อย่างหนึ่งคือการใช้ ListIterator ใน a for loop มันไม่สะอาดเท่าไวยากรณ์ของลำไส้ใหญ่ แต่ใช้งานได้

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 ไปที่"วิธีวนซ้ำในรายการใน Java"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.