जावा में रिवर्स ऑर्डर में एक सूची के माध्यम से Iterating


251

मैं जेनरिक का उपयोग करने के लिए कोड का एक टुकड़ा माइग्रेट कर रहा हूं। ऐसा करने का एक तर्क यह है कि लूप के लिए अनुक्रमणिका पर नज़र रखने, या स्पष्ट इट्रेटर का उपयोग करने की तुलना में बहुत क्लीनर है।

लगभग आधे मामलों में, सूची (एक एरेलेस्ट) को आज एक सूचकांक का उपयोग करके रिवर्स ऑर्डर में पुनरावृत्त किया जा रहा है।

क्या कोई ऐसा करने का एक साफ तरीका सुझा सकता है (जब से मैं indexed for loopसंग्रह के साथ काम करना पसंद नहीं करता), हालांकि यह काम करता है?

 for (int i = nodes.size() - 1; i >= 0; i--) {
    final Node each = (Node) nodes.get(i);
    ...
 }

नोट: मैं JDK के बाहर कोई नई निर्भरता नहीं जोड़ सकता।


10
एक अनुक्रमित डेटा संरचना पर पुनरावृति करने के लिए एक स्पष्ट सूचकांक का उपयोग करने के बारे में इतना बुरा क्या है? कम से कम यह आपको दिखाता है कि वास्तव में क्या चल रहा है। पीछे की ओर पुनरावृत्त करने के लिए मैं हमेशा निम्नलिखित थोड़ा छोटा मुहावरा:for (int i = nodes.size(); --i >= 0;)
x4u

4
विशेष रूप से कुछ भी नहीं, मैं सिर्फ एक इंटरफ़ेस के लिए कार्यक्रम करूंगा और यह नहीं जानूंगा कि मैं किस तरह की सूची का उपयोग कर रहा हूं। हालांकि मुझे आपका छोटा हाथ बहुत पसंद है। (+1 टिप्पणी)
एलन लालडे

1
@ x4u: इसमें बहुत अधिक नहीं है, हालांकि Iterator विफल-तेज़ है और यह तत्वों को पुनरावृत्ति के दौरान आसानी से निकालने की अनुमति देता है।
अदाम्सकी

2
यह वर्ग टूट गया है, क्योंकि उपयोगकर्ता एक ही Iterable पर दूसरी बार पुनरावृति करना चाह सकता है, या जब पुनरावृत्ति का निर्माण किया जाता है और जब इसे पुनरावृत्त किया जाता है, तो सूची बदल सकती है। मुझे यकीन है कि आप अपने उद्देश्यों के लिए सुनिश्चित करेंगे कि आप ऐसा न करें, लेकिन कोड को ठीक करना बहुत कठिन नहीं होगा; या सिर्फ अमरूद (अपाचे 2.0 लाइसेंस) से कोड चोरी करें: code.google.com/p/guava-lbooks/source/browse/trunk/src/com/…
केविन बॉरिलिन

1
काफी उचित है, लेकिन यहां तक ​​कि अमरूद एक ही बात के लिए अतिसंवेदनशील है अगर मैं इसे सही पढ़ रहा हूं। परिणाम की एक प्रति को रिवर्स रखने के लिए एक उपयोगकर्ता थे, यह एक ही समस्या होगी।
एलन लालोंडे

जवाबों:


447

इसे इस्तेमाल करे:

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

4
बुरा नहीं। सूचकांक का उपयोग नहीं करता है, लेकिन प्रत्येक वाक्यविन्यास के लिए लालित्य खो देता है। वैसे भी +1।
एलन लालोंडे

26
सूची सूचकांक के बिना कॉलिंग सूची () को सूची की शुरुआत में एक Iterator देगा और इसलिए पहली बार कॉल करने पर गलत () वापस आ जाएगी।
एडम्सकी

2
आपको listIteratorकॉल पर एक इंडेक्स चाहिए , मुझे लगता है।
टॉम हॉल्टिन -

1
आप एक Iteratorका उपयोग कर सकते हैं कि ListIteratorरिवर्स में एक का उपयोग करता है , लेकिन यह एक लूप के लिए इसके लायक नहीं हो सकता है।
टॉम हॉल्टिन -

1
यह एक लूप नहीं है, इसलिए मैंने इसे लिया है और इसे लपेटा है। pastebin.ca/1759041 तो, अब मैं कर सकता हूँfor (Node each : new ListReverse<Node>(nodes)) { }
एलन लालडे

35

अमरूद की पेशकश Lists#reverse(List)और ImmutableList#reverse()। जैसे कि अमरूद के अधिकांश मामलों में, पूर्व प्रतिनिधि को उत्तरार्द्ध में भेज देता है यदि तर्क एक है ImmutableList, तो आप सभी मामलों में पूर्व का उपयोग कर सकते हैं। ये सूची की नई प्रतियां नहीं बनाते हैं बल्कि इसके "उलटे विचार" होते हैं।

उदाहरण

List reversed = ImmutableList.copyOf(myList).reverse();

23

मुझे नहीं लगता कि लूप सिंटैक्स का उपयोग करना संभव है। केवल एक चीज जो मैं सुझा सकता हूं, वह है:

Collections.reverse(list);
for (Object o : list) {
  ...
}

... लेकिन मैं यह नहीं कहूंगा कि यह "क्लीनर" है क्योंकि यह कम कुशल होने जा रहा है।


26
और यह उस स्थान को भी बदलता है जिसे आप पार करते हैं, जो काफी बड़ा दुष्प्रभाव है। (कहते हैं कि आप इसे एक विधि में लपेटते हैं, हर बार जब आप सूची को दूसरे तरीके से कहते हैं ^ ^)
जॉलिवियर

यह सबसे साफ उपाय है।
jfajunior

14

विकल्प 1: क्या आपने सूची को उल्टा करने के बारे में सोचा है # रिवर्स () और फिर फोरच का उपयोग करते हुए?

बेशक, आप अपने कोड को भी फिर से भरना चाहते हैं ताकि सूची को सही ढंग से ऑर्डर किया जा सके ताकि आपको इसे उल्टा न करना पड़े, जो अतिरिक्त स्थान / समय का उपयोग करता है।


संपादित करें:

विकल्प 2: वैकल्पिक रूप से, आप एक ArrayList के बजाय एक Deque का उपयोग कर सकते हैं? यह आपको आगे और पीछे की ओर पुनरावृत्त करने की अनुमति देगा


संपादित करें:

विकल्प 3: जैसा कि दूसरों ने सुझाव दिया है, आप एक Iterator लिख सकते हैं जो सूची के माध्यम से रिवर्स में जाएगा, यहां एक उदाहरण है:

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

1
इसके बारे में सोचा है, लेकिन इसे उलटने की लागत निषेधात्मक है। इसके अलावा, इसे केवल आधे समय के लिए इस तरह की पुनरावृत्ति की आवश्यकता होती है। इसे फ़्लिप करने से समस्या दूसरे हाफ़ में चली जाएगी।
एलन लोंडे

3
+ Deque के लिए; विशिष्ट कार्यान्वयन हैं descendingIterator()
ट्रैशगोड

यह लिंक्ड सूचियों के लिए भयानक प्रदर्शन करेगा, ओपी का संस्करण बेहतर है।
मास्टरएक्सिलो

1
एक for eachअभिव्यक्ति का उपयोग करना मेरी राय में सबसे मुहावरेदार समाधान है। यह महसूस करना अच्छा है कि यह संभव है अगर आपकी सूची एक तरह से Iterable को लागू करती है जो पीछे की ओर पुनरावृत्त होती है। मैं इस दृष्टिकोण का उपयोग करने और Apache Commons संग्रह से ReverseListIterator वर्ग का उपयोग करने जा रहा हूं।
डेविड ग्रूमेस

11

आप LinkedListसामान्य इंटरफ़ेस के बजाय ठोस वर्ग का उपयोग कर सकते हैं List। तो फिर तुम descendingIteratorरिवर्स दिशा के साथ पुनरावृत्ति के लिए है।

LinkedList<String > linkedList;
for( Iterator<String > it = linkedList.descendingIterator(); it.hasNext(); ) {
    String text = it.next();
}

पता नहीं क्यों descendingIteratorसाथ नहीं है ArrayList...


9

यह एक पुराना प्रश्न है, लेकिन इसमें java8 के अनुकूल उत्तर का अभाव है। स्ट्रीमिंग एपीआई की मदद से सूची को रिवर्स-इट्रेट करने के कुछ तरीके इस प्रकार हैं:

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

2
बहुत अच्छा, बस एक कॉस्मेटिक परिवर्तन: int size = list.size (); ListIterator <Integer> it = list.listIterator (size); Stream.generate (यह :: पिछले) .limit (आकार) .forEach (System.out :: println);
22

5

यहाँ एक (अप्रयुक्त) का कार्यान्वयन है 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()));
  }
}

मेरे लिए सही लगता है, हालांकि इसे सार्वजनिक निर्माता के बजाय एक स्थिर विधि के रूप में उजागर करना (ज्यादातर मामलों में) क्लाइंट के लिए टाइप पैरामीटर को निर्दिष्ट करने की आवश्यकता से बचना होगा। ऐसा ही अमरूद करता है ..
केविन बॉर्रिल

2
यह सबसे अच्छा कार्यान्वयन है, हालांकि ReverseIteratorआवश्यक निर्माता को याद कर रहा है और कोड को Listइसके बजाय उपयोग करना चाहिए ArrayList
एंडी

5

यदि सूचियां काफी छोटी हैं, ताकि प्रदर्शन एक वास्तविक मुद्दा नहीं है, तो कोई reverse-class के -metod का उपयोग कर सकता Listsहै Google Guava। बहुत सुंदर उपज देता है 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
}

5

एक प्रथा बनाएँ reverseIterable


पता नहीं क्यों यह नीचे हो रही है मतदान, मैं बस सूची है कि यह करता है के लिए एक आवरण बना सकता है।
एलन लोंडे

यह कलेक्शन.रेवर्स () आईएमएचओ को कॉल करने से कम साफ नहीं है।
एडम्सकी

@ सभी: पुनः सहमत। डाउनवोट्स, हालांकि मैं यह नहीं देखता कि आप कॉल क्यों देखें लिस्टइंटरेटर (list.size ()) के रूप में "साफ नहीं"। यहां तक ​​कि अगर आप इसे लपेटते हैं, तब भी आप उसी विधि को कहीं कॉल करने के लिए कर रहे हैं।
एडम्सकी

मान लीजिए, सिर्फ सफाई के लिए, प्रदर्शन हिट लेने के लिए तैयार नहीं है।
एलन लालोंडे

18
वोट दिया क्योंकि मुझे नहीं लगता कि यह एक पूर्ण जवाब है।
मार्टन बॉड्यूज

3

बहुत सरल उदाहरण:

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) {
  ...
}



1

कोड है जो इस तरह दिखता है:

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

1
यह धागे में कहीं और उल्लेख किया गया है, लेकिन listIteratorक्षेत्र को Iteratorकार्यान्वयन के अंदर होना चाहिए, कार्यान्वयन नहीं Iterable
एंडी

1
एक वर्ग नहीं, एक एनुम प्रकार का उपयोग क्यों करें?
ऑबिन

1
यह एक निजी डिफ़ॉल्ट कंस्ट्रक्टर लिखने के बिना 'इन' क्लास को गैर-इंस्टेंटेबल बनाता है। उन कक्षाओं के लिए अच्छा है जिनमें केवल स्थिर विधियाँ हैं।
intrepidis

मैं कहूंगा कि निजी डिफॉल्ट कंस्ट्रक्टर को लिखने से बचने के लिए केवल गाली देने से दुश्मनी होती है। कक्षा के लिए एनम और कक्षाओं के लिए एनम का उपयोग कैसे करें? एक वर्ग में एक एनम बनाने से यह भी उदाहरण के लिए एक name()विधि, एक ordinal()विधि और एक static valueOf()विधि हो जाता है ।
JHH

1
हां, आप अपनी राय जारी रख सकते हैं और यह ठीक भी काम करेगा, लेकिन मुझे लगता है कि इसके विपरीत। कक्षाएं वस्तुओं को तात्कालिक करने के लिए होती हैं और उनकी तात्कालिकता को बाधित करने के लिए एक निजी डिफ़ॉल्ट कंस्ट्रक्टर को शामिल करके उनका दुरुपयोग किया जाता है। जावा में, एनम वास्तव में कक्षाएं हैं, लेकिन वे जो कभी भी डिजाइन द्वारा त्वरित नहीं किए जा सकते हैं।
intrepidis

0

के रूप में कम से कम सुझाव दिया गया है दो बार, तो आप उपयोग कर सकते हैं descendingIteratorएक साथ Dequeएक साथ विशेष रूप से, LinkedList। यदि आप प्रत्येक लूप (यानी, 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);
        }
    }
}

-10

कारण: "पता नहीं क्यों ArrayList के साथ कोई अवरोही नहीं है ..."

चूंकि सरणी सूची में सूची को उसी क्रम में नहीं रखा गया है जैसा कि सूची में डेटा जोड़ा गया है। इसलिए, कभी भी Arraylist का उपयोग न करें।

लिंक की गई सूची में डेटा को ADD के समान क्रम में रखा जाएगा।

इसलिए, मेरे उदाहरण में, मैंने अपने दिमाग को मोड़ने के लिए उपयोगकर्ता को बनाने और उन्हें अपनी ओर से कुछ कसरत करने के लिए 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) {
  ...
}

4
"चूंकि सरणी सूची में सूची को उसी क्रम में नहीं रखा गया है जैसा कि डेटा को सूची में जोड़ा गया है" umm, हां यह करता है, कम से कम यह उसी तरह से करता है जैसे एक लिंक की गई सूची करती है। क्या आप इसका उदाहरण दे सकते हैं कि वे कैसे नहीं करते?
corsiKa

हाँ, ArrayList और LinkedList (सामान्य रूप से सूची अनुबंध) क्रम में आइटम सम्मिलित करते हैं। सेट अनुबंध अनियंत्रित या सॉर्ट (ट्रीसेट) द्वारा आदेशित है। उल्टा विचार बुरा नहीं है, लेकिन याद रखें कि यह वास्तव में उस सूची को फिर से व्यवस्थित करता है जो धीमी हो सकती है।
बिल के

2
मैंने इस उत्तर को अस्वीकार कर दिया क्योंकि यह गलत तरीके से बताता है कि ArrayLists प्रविष्टि क्रम में आइटम नहीं रखते हैं। @ बिल-के नोट्स के रूप में, सभी सूचियों को परिभाषा के अनुसार संग्रह का आदेश दिया गया है। इसके बजाय एक लिंक्डलिस्ट का उपयोग करने के सुझाव में योग्यता है, क्योंकि इसमें अवरोही () विधि शामिल है, लेकिन केवल अगर प्रदर्शन स्मृति और अमूर्तता की तुलना में एक बड़ी चिंता है।
माइकल शेपर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.