Her döngü için javada ters sırada yapılabilir mi?


151

Java kullanarak bir Listeyi ters sırada çalıştırmam gerekiyor.

Peki bu nereye gidiyor:

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

Her sözdizimi için kullanarak stringList'i ters sırada yinelemenin bir yolu var mı ?

Açıklık getirmek için: Bir listeyi ters sırada nasıl yineleyeceğimi biliyorum, ancak (merak uğruna) her stil için nasıl yapılacağını bilmek istiyorum .


6
"Her biri için" döngüsünün amacı, yalnızca her öğe üzerinde bir işlem gerçekleştirmeniz gerektiğidir ve sıranın önemli olmamasıdır. Çünkü her biri, öğeleri tamamen rastgele sırayla işleyebilirdi ve yine de bunun için tasarlandığı şeyi yapıyor olurdu. Öğeleri belirli bir şekilde işlemeniz gerekiyorsa, bunu manuel olarak yapmanızı öneririm.
muusbolla

Java koleksiyonları kitaplığı. Dille hiçbir ilgisi yok. Josh Bloch'u suçla.
Tom Hawtin - tackline

7
@muusbolla: Ama Liste sıralı bir koleksiyon olduğu için, emri ne olursa olsun kesinlikle yerine getirilecek? Bu nedenle, her biri için bir Listenin öğelerini rastgele bir sırayla işlemez.
Lee Kowalkowski

5
@muusbolla bu doğru değil. Belki Settüretilmiş koleksiyonlar durumunda . Koleksiyon yönteminden foreachdönen yineleyicinin sırasına göre yinelemeyi garanti eder iterator(). docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
robert

Yanıtlar:


154

Collections.reverse yöntemi aslında orijinal listenin öğelerinin ters sırada kopyalanmış olduğu yeni bir liste döndürür, bu nedenle bu, orijinal listenin boyutuna göre O (n) performansına sahiptir.

Daha verimli bir çözüm olarak, bir Listenin tersine çevrilmiş görünümünü Yinelenebilir olarak sunan bir dekoratör yazabilirsiniz. Dekoratörünüz tarafından döndürülen yineleyici, öğeler üzerinde ters sırada gezinmek için dekore edilmiş listenin ListIterator'ını kullanır.

Örneğin:

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

Ve bunu şu şekilde kullanırsınız:

import static Reversed.reversed;

...

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

22
Temelde Google'ın Iterables.reverse yaptığı şey bu, evet :)
Jon Skeet

10
Jon'un cevabını kabul etmemiz gereken bir 'kural' olduğunu biliyorum :) ama .. bunu kabul etmek istiyorum (esasen aynı olsalar bile) çünkü başka bir üçüncü taraf kitaplığı eklememi gerektirmiyor ( bazıları bu nedenin OO'nun başlıca avantajlarından birini - yeniden kullanılabilirliği - kırdığını iddia edebilir.
Ron Tuffin

Küçük hata: public void remove () 'de bir return ifadesi olmamalı, sadece şöyle olmalıdır: i.remove ();
Jesper

10
Collections.reverse () tersine çevrilmiş bir kopya döndürmez, ancak parametre olarak kendisine iletilen Listeye göre hareket eder. Yineleyici ile çözümünüz gibi. Gerçekten zarif.
er4z0r

98

Liste için Google Guava Kitaplığını kullanabilirsiniz :

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

Not gelmez bütün koleksiyonunu ters veya böyle bir şey yok - bu sadece ters sırada, iterasyon ve rastgele erişim sağlar. Bu, önce koleksiyonu tersine çevirmekten daha etkilidir.Lists.reverse

Keyfi bir yinelenebilirliği tersine çevirmek için hepsini okumanız ve ardından geriye doğru "yeniden oynatmanız" gerekir.

(Henüz kullanmıyorsanız, Guava'ya iyice bir göz atmanızı tavsiye ederim . Harika bir şey.)


1
Kod tabanımız, larvalabs ( larvalabs.com/collections ) tarafından yayınlanan Commons Koleksiyonlarının jenerikleştirilmiş versiyonunu yoğun bir şekilde kullanır . Apache Commons için SVN deposuna bakıldığında, Commons Collections'ın java 5 sürümünü yayınlama çalışmalarının çoğunun yapıldığı açıktır, ancak henüz yayınlamadılar.
skaffman

Bunu sevdim. Bu kadar kullanışlı olmasaydı, buna fiş derdim.
geowa4

Jakarta'nın neden Apache Commons'ı güncelleme zahmetine girmediğini merak ediyorum.
Uri

3
Onu güncellediler, ben de bunu söylüyorum. Sadece yayınlamadılar.
skaffman

23
Iterables.reverse kullanımdan kaldırıldı, bunun yerine Lists.reverse veya ImmutableList.reverse'i kullanın.
Garrett Hall

40

Liste (Set'in aksine) sıralı bir koleksiyondur ve üzerinde yinelendiğinde, siparişi sözleşmeye göre korur. Bir Yığın ters sırada yinelenmesini beklerdim ama maalesef öyle değil. Yani düşünebildiğim en basit çözüm şudur:

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

Bunun "her biri için" döngü çözümü olmadığının farkındayım. Google Koleksiyonları gibi yeni bir kitaplık sunmaktansa for döngüsünü kullanmayı tercih ederim.

Collections.reverse () de işi yapar ancak bir kopyayı ters sırada döndürmek yerine listeyi günceller.


3
Bu yaklaşım, dizi tabanlı listeler için iyi olabilir (ArrayList gibi), ancak Bağlantılı Listeler için alt optimal olacaktır, çünkü her bir alım için listeyi baştan sona (veya muhtemelen sondan başa) geçmek zorunda kalacaktır. Nat'ın çözümünde olduğu gibi daha akıllı bir yineleyici kullanmak daha iyidir (List'in tüm uygulamaları için idealdir).
Chris

1
Dahası, for eachsözdizimini açıkça isteyen OP'deki talepten sapıyor
Paul W

8

Bu, orijinal listeyi karıştırır ve ayrıca döngünün dışında çağrılması gerekir. Ayrıca her döngüde bir ters çevirme yapmak istemezsiniz - bunlardan biri Iterables.reverse ideasuygulanmış olsaydı bu doğru olur muydu?

Collections.reverse(stringList);

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

5

AFAIK, standart kitaplıkta, her bir sözdizimi için destekleyen standart bir "ters_itleyici" türü yoktur ki bu zaten dile geç getirdikleri sözdizimsel bir şekerdir.

(Item element: myList.clone (). Reverse ()) gibi bir şey yapabilir ve ilgili fiyatı ödeyebilirsiniz.

Bu aynı zamanda, size pahalı işlemler yapmanın uygun yollarını sunmama fenomeni ile oldukça tutarlı görünüyor - çünkü bir liste, tanımı gereği, O (N) rastgele erişim karmaşıklığına sahip olabilir (arayüzü tek bağlantıyla uygulayabilirsiniz), tersine yineleme O (N ^ 2) olabilir. Tabii bir ArrayList'iniz varsa, bu bedeli ödemezsiniz.


Bir Yineleyici içinde sarmalanabilen bir ListIterator'ı geriye doğru çalıştırabilirsiniz.
Tom Hawtin - tackline

@Tom: İyi nokta. Bununla birlikte, yineleyici ile döngüler için hala sinir bozucu eski stili yapıyorsunuz ve başlamak için son öğeye ulaşmak için yine de maliyeti ödeyebilirsiniz ... Cevabıma niteleyiciyi ekledim, yine de teşekkürler.
Uri

Deque bir ters yineleyiciye sahiptir.
Michael Munsey

2

Bu bir seçenek olabilir. Umarım son öğeden başlamak için döngüden sonuna kadar başlamaktan daha iyi bir yol vardır.

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

2

İtibariyle comment : Apache Commons kullanmaya muktedir olmalıdırReverseListIterator

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

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

Gibi @rogerdpack dedi , sen sarmak gerekiyor ReverseListIteratorbir şekilde Iterable.


1

Öğeleri sizin için tersine çevirecek bir numaralandırıcı verecek özel bir kod yazmadan olmaz.

Öğeleri ters sırayla döndürecek özel bir Yinelenebilir uygulaması oluşturarak bunu Java'da yapabilmelisiniz.

Daha sonra, sarmalayıcıyı başlatırsınız (veya yöntemi çağırırsınız, neye sahip olursunuz), bu da her döngü için öğedeki öğeyi tersine çeviren Yinelenebilir uygulamayı döndürür.


1

Kutudan çıkan her sözdizimi için kullanmak ve ters sırada gitmek istiyorsanız, koleksiyonunuzu tersine çevirmeniz gerekir.


1

Yukarıdaki tüm yanıtlar, yalnızca başka bir yöntemi paketleyerek veya dışarıdan bazı yabancı kodu çağırarak gereksinimi karşılar;

İşte Thinking in Java 4th edition , bölüm 11.13.1 AdapterMethodIdiom'dan kopyalanan çözüm ;

İşte kod:

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

neden int current = size() - 1doğru neden olmasın int current = this.size() - 1yaint current = super.size() - 1
qiwen li

1

Etrafında bir çalışma:

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

Veya guava ile :

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


0

Bu soruya kesinlikle geç bir cevap. Bir olasılık, ListIterator'ı bir for döngüsünde kullanmaktır. İki nokta üst üste sözdizimi kadar temiz değil, ama işe yarıyor.

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 sözdizimi için kredi "Java'da bir liste üzerinde yineleme yolları" na gider

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.