जावा में एक सूची पर पुनरावृति करने के तरीके


576

जावा भाषा में कुछ नया होने के नाते मैं खुद को उन सभी तरीकों (या कम से कम गैर-पैथोलॉजिकल लोगों) से परिचित करने की कोशिश कर रहा हूं जो किसी सूची (या शायद अन्य संग्रह) और प्रत्येक के फायदे या नुकसान के माध्यम से पुनरावृत्त हो सकते हैं।

किसी List<E> listवस्तु को देखते हुए , मुझे सभी तत्वों के माध्यम से लूप करने के निम्नलिखित तरीकों के बारे में पता है:

लूप के लिए बुनियादी (बेशक, वहाँ बराबर / छोरों के रूप में अच्छी तरह से कर रहे हैं)whiledo while

// Not recommended (see below)!
for (int i = 0; i < list.size(); i++) {
    E element = list.get(i);
    // 1 - can call methods of element
    // 2 - can use 'i' to make index-based calls to methods of list

    // ...
}

नोट: जैसा कि @amarseillan ने बताया, यह फ़ॉर्म Lists पर पुनरावृत्ति करने के लिए एक खराब विकल्प है , क्योंकि getविधि का वास्तविक कार्यान्वयन किसी का उपयोग करते समय उतना कुशल नहीं हो सकता है Iterator। उदाहरण के लिए, LinkedListकार्यान्वयन से पहले होने वाले सभी तत्वों को आई-वें तत्व को प्राप्त करना होगा।

उपरोक्त उदाहरण में Listभविष्य के पुनरावृत्तियों को अधिक कुशल बनाने के लिए "अपनी जगह को बचाने" के कार्यान्वयन के लिए कोई रास्ता नहीं है । इसके लिए ArrayListयह वास्तव में मायने नहीं रखता है, क्योंकि जटिलता / लागत getनिरंतर समय (O (1)) है, जबकि एक के LinkedListलिए यह सूची के आकार (O (n)) के समानुपाती है।

अंतर्निहित Collectionsकार्यान्वयन की कम्प्यूटेशनल जटिलता के बारे में अधिक जानकारी के लिए , इस प्रश्न को देखें

लूप के लिए बढ़ाया ( इस प्रश्न में अच्छी तरह से समझाया गया है )

for (E element : list) {
    // 1 - can call methods of element

    // ...
}

इटरेटर

for (Iterator<E> iter = list.iterator(); iter.hasNext(); ) {
    E element = iter.next();
    // 1 - can call methods of element
    // 2 - can use iter.remove() to remove the current element from the list

    // ...
}

ListIterator

for (ListIterator<E> iter = list.listIterator(); iter.hasNext(); ) {
    E element = iter.next();
    // 1 - can call methods of element
    // 2 - can use iter.remove() to remove the current element from the list
    // 3 - can use iter.add(...) to insert a new element into the list
    //     between element and iter->next()
    // 4 - can use iter.set(...) to replace the current element

    // ...
}

कार्यात्मक जावा

list.stream().map(e -> e + 1); // Can apply a transformation function for e

Iterable.forEach , Stream.forEach , ...

(जावा 8 की स्ट्रीम एपीआई से एक मानचित्र विधि (देखें @ i_am_zero का उत्तर)।)

जावा 8 संग्रह कक्षाओं में जो लागू होते हैं Iterable(उदाहरण के लिए, सभी Listएस) में अब एक forEachविधि है, जिसका उपयोग ऊपर दिखाए गए लूप स्टेटमेंट के लिए किया जा सकता है। (यहां एक और सवाल है जो एक अच्छी तुलना प्रदान करता है।)

Arrays.asList(1,2,3,4).forEach(System.out::println);
// 1 - can call methods of an element
// 2 - would need reference to containing object to remove an item
//     (TODO: someone please confirm / deny this)
// 3 - functionally separates iteration from the action
//     being performed with each item.

Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
// Same capabilities as above plus potentially greater
// utilization of parallelism
// (caution: consequently, order of execution is not guaranteed,
// see [Stream.forEachOrdered][stream-foreach-ordered] for more
// information about this).

अन्य तरीके क्या हैं, यदि कोई हो?

(BTW, मेरी रुचि प्रदर्शन को अनुकूलित करने की इच्छा से बिल्कुल भी नहीं है , मैं सिर्फ यह जानना चाहता हूं कि एक डेवलपर के रूप में मेरे लिए कौन से रूप उपलब्ध हैं।)


1
वे गैर-रोगविज्ञानी हैं, हालांकि आप संग्रह को संसाधित करने के लिए कई कार्यात्मक शैली पुस्तकालयों में से किसी का भी उपयोग कर सकते हैं।
डेव न्यूटन

Listइंटरफ़ेस के बारे में विशेष रूप से सवाल है ?
सोटिरिओस डेलिमोनोलिस

@ SotiriosDelimanolis, सभी इरादों और उद्देश्यों के लिए, हाँ यह <कोड> सूची </ कोड> के लिए विशिष्ट है, लेकिन अगर कोई अन्य दिलचस्प तरीके से काम करना है, तो कहें, एक <code> Collection </ code>, मैं होगा। उन्हें जानने के लिए दिलचस्पी है।
IX3

@DaveNewton, इस विचार के लिए धन्यवाद। मैंने ऐसा कुछ भी कभी इस्तेमाल नहीं किया है। कृपया मेरे संपादित प्रश्न पर एक नज़र डालें और मुझे बताएं कि क्या मुझे समझ में आया कि आपका क्या मतलब है।
IX3

2
@sdasdadas, किया: stackoverflow.com/questions/18410035/…
iX3

जवाबों:


265

लूपिंग के तीन रूप लगभग समान हैं। बढ़ाया forलूप:

for (E element : list) {
    . . .
}

के अनुसार, जावा भाषा विशिष्टता , समान एक पारंपरिक वाले पुनरावर्तक की स्पष्ट उपयोग करने के लिए प्रभाव में forपाश। तीसरे मामले में, आप केवल वर्तमान तत्व को हटाकर सूची सामग्री को संशोधित कर सकते हैं, और, केवल तब जब आप इसे removeपुनरावृत्ति करने वाले की विधि के माध्यम से करते हैं । इंडेक्स-आधारित पुनरावृत्ति के साथ, आप किसी भी तरह से सूची को संशोधित करने के लिए स्वतंत्र हैं। हालांकि, मौजूदा सूचकांक में आने वाले तत्वों को जोड़ना या हटाना आपके लूप स्किपिंग तत्वों या एक ही तत्व को कई बार संसाधित करने में जोखिम है; जब आप इस तरह के बदलाव करते हैं तो आपको लूप इंडेक्स को ठीक से समायोजित करने की आवश्यकता होती है।

सभी मामलों में, elementवास्तविक सूची तत्व का संदर्भ है। कोई भी पुनरावृति विधियाँ सूची में किसी भी चीज़ की प्रतिलिपि नहीं बनाती हैं। elementसूची में संबंधित तत्व की आंतरिक स्थिति में हमेशा आंतरिक स्थिति में परिवर्तन देखा जाएगा।

अनिवार्य रूप से, सूची पर पुनरावृति करने के केवल दो तरीके हैं: एक सूचकांक का उपयोग करके या एक पुनरावृत्त का उपयोग करके। लूप के लिए संवर्धित केवल जावा में 5 में पेश किए गए एक सिंटैक्टिक शॉर्टकट है जो स्पष्ट रूप से एक इटेरियर को परिभाषित करने के टेडियम से बचने के लिए है। दोनों शैलियों के लिए, आप अनिवार्य रूप से तुच्छ विविधताओं का उपयोग करके आ सकते हैं for, whileया do whileब्लॉक कर सकते हैं, लेकिन वे सभी एक ही चीज़ (या, बल्कि दो चीजों) को उबालते हैं।

संपादित करें: जैसा कि @ iX3 एक टिप्पणी में बताते हैं, आप ListIteratorसूची के वर्तमान तत्व को सेट करने के लिए उपयोग कर सकते हैं जैसे कि आप पुनरावृत्ति कर रहे हैं। आपको लूप वैरिएबल को इनिशियलाइज़ करने के List#listIterator()बजाय उपयोग करने की आवश्यकता होगी List#iterator()(जो कि, जाहिर है, इसे एक के ListIteratorबजाय घोषित किया जाना होगा Iterator)।


ठीक है, धन्यवाद, लेकिन अगर इट्रेटर वास्तव में वास्तविक तत्व (कॉपी नहीं) के संदर्भ में लौट रहा है तो मैं इसका उपयोग सूची में मूल्य बदलने के लिए कैसे कर सकता हूं? मैं यह लेता हूं कि अगर मैं एक का उपयोग करता हूं e = iterator.next()तो e = somethingElseबस उस वस्तु eको बदल देता हूं जो वास्तविक स्टोर को बदलने के बजाय वस्तु का संदर्भ देता है जिसमें iterator.next()से मूल्य को पुनः प्राप्त किया है।
IX3

@ IX3 - यह सूचकांक आधारित पुनरावृत्ति के रूप में अच्छी तरह से सच होगा; eसूची में क्या है, इसे बदलने के लिए एक नई वस्तु निर्दिष्ट नहीं करना; आपको फोन करना होगा list.set(index, thing)। आप बदल सकते हैं सामग्री की e(जैसे, e.setSomething(newValue)), लेकिन परिवर्तन क्या तत्व सूची में संग्रहीत के रूप में आप यह पुनरावृत्ति कर रहे हैं, तो आप एक सूचकांक आधारित यात्रा के साथ छड़ी की जरूरत है।
टेड हॉप

उस स्पष्टीकरण के लिए धन्यवाद; मुझे लगता है कि मैं समझ रहा हूं कि आप अभी क्या कह रहे हैं और मेरे प्रश्न / टिप्पणियों को तदनुसार अपडेट करेंगे। प्रति-से "कोई भी प्रतिलिपि नहीं है", लेकिन भाषा डिज़ाइन के कारण eमुझे I की सामग्री में परिवर्तन करने के लिए किसी एक eविधि को कॉल करना होगा क्योंकि असाइनमेंट सिर्फ पॉइंटर को बदलता है (मेरा सी क्षमा करें)।
20

तो जैसे अपरिवर्तनीय प्रकारों की सूचियों के लिए Integerऔर Stringसामग्री for-eachया Iteratorविधियों का उपयोग करके सामग्री को बदलना संभव नहीं होगा - तत्वों को बदलने के लिए सूची वस्तु को ही हेरफेर करना होगा। क्या वह सही है?
IX3

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

46

प्रश्न में सूचीबद्ध प्रत्येक प्रकार का उदाहरण:

ListIterationExample.java

import java.util.*;

public class ListIterationExample {

     public static void main(String []args){
        List<Integer> numbers = new ArrayList<Integer>();

        // populates list with initial values
        for (Integer i : Arrays.asList(0,1,2,3,4,5,6,7))
            numbers.add(i);
        printList(numbers);         // 0,1,2,3,4,5,6,7

        // replaces each element with twice its value
        for (int index=0; index < numbers.size(); index++) {
            numbers.set(index, numbers.get(index)*2); 
        }
        printList(numbers);         // 0,2,4,6,8,10,12,14

        // does nothing because list is not being changed
        for (Integer number : numbers) {
            number++; // number = new Integer(number+1);
        }
        printList(numbers);         // 0,2,4,6,8,10,12,14  

        // same as above -- just different syntax
        for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            number++;
        }
        printList(numbers);         // 0,2,4,6,8,10,12,14

        // ListIterator<?> provides an "add" method to insert elements
        // between the current element and the cursor
        for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            iter.add(number+1);     // insert a number right before this
        }
        printList(numbers);         // 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15

        // Iterator<?> provides a "remove" method to delete elements
        // between the current element and the cursor
        for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            if (number % 2 == 0)    // if number is even 
                iter.remove();      // remove it from the collection
        }
        printList(numbers);         // 1,3,5,7,9,11,13,15

        // ListIterator<?> provides a "set" method to replace elements
        for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
            Integer number = iter.next();
            iter.set(number/2);     // divide each element by 2
        }
        printList(numbers);         // 0,1,2,3,4,5,6,7
     }

     public static void printList(List<Integer> numbers) {
        StringBuilder sb = new StringBuilder();
        for (Integer number : numbers) {
            sb.append(number);
            sb.append(",");
        }
        sb.deleteCharAt(sb.length()-1); // remove trailing comma
        System.out.println(sb.toString());
     }
}

23

मूल लूप की अनुशंसा नहीं की जाती है क्योंकि आप सूची के कार्यान्वयन को नहीं जानते हैं।

यदि वह एक लिंक्डलिस्ट था, तो प्रत्येक को कॉल करना था

list.get(i)

सूची में पुनरावृत्ति होगी, जिसके परिणामस्वरूप N ^ 2 समय की जटिलता होगी।


1
सही बात; यह एक अच्छा बिंदु है, और मैं प्रश्न में उदाहरण को अपडेट करूंगा।
IX3


5
उस पोस्ट में जो मैंने पढ़ा है, वही मैंने कहा है ... आप वास्तव में क्या हिस्सा पढ़ रहे हैं?
अमरसीलन

20

JDK8- शैली पुनरावृत्ति:

public class IterationDemo {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3);
        list.stream().forEach(elem -> System.out.println("element " + elem));
    }
}

धन्यवाद, मैं अंत में जावा 8 समाधानों के साथ शीर्ष पर सूची को अपडेट करने का इरादा रखता हूं।
IX3

1
@ यूजीन 82 क्या यह दृष्टिकोण अधिक कुशल है?
nazar_art

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

7

में जावा 8 हम संग्रह वर्गों पर पुनरावृति करने के लिए कई तरीके हैं।

Iterable forEach का उपयोग करना

लागू करने वाले संग्रह Iterable(उदाहरण के लिए सभी सूचियों) में अब forEachविधि है। हम जावा 8 में शुरू की गई विधि-संदर्भ का उपयोग कर सकते हैं ।

Arrays.asList(1,2,3,4).forEach(System.out::println);

धाराओं का उपयोग forEach और forEachOrdered

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

Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
Arrays.asList(1,2,3,4).stream().forEachOrdered(System.out::println);

हमें forEachOrderedअधिक पसंद करना चाहिए forEachक्योंकि व्यवहार का forEachस्पष्ट रूप से nondeterministic है जहां forEachOrderedधारा के एनकाउंटर क्रम में इस स्ट्रीम के प्रत्येक तत्व के लिए एक क्रिया करता है, अगर स्ट्रीम में एक एनकाउंटर ऑर्डर है। इसलिए forEach इस बात की गारंटी नहीं देता कि ऑर्डर रखा जाएगा।

धाराओं के साथ लाभ यह है कि हम जहां भी उचित हो, समानांतर धाराओं का उपयोग कर सकते हैं। यदि उद्देश्य केवल आदेश के बावजूद वस्तुओं को प्रिंट करना है, तो हम समानांतर धारा का उपयोग इस प्रकार कर सकते हैं:

Arrays.asList(1,2,3,4).parallelStream().forEach(System.out::println);

5

मुझे नहीं पता कि आप पैथोलॉजिकल क्या मानते हैं, लेकिन मुझे कुछ विकल्प प्रदान करें जो आप पहले नहीं देख सकते थे:

List<E> sl= list ;
while( ! sl.empty() ) {
    E element= sl.get(0) ;
    .....
    sl= sl.subList(1,sl.size());
}

या इसके पुनरावर्ती संस्करण:

void visit(List<E> list) {
    if( list.isEmpty() ) return;
    E element= list.get(0) ;
    ....
    visit(list.subList(1,list.size()));
}

इसके अलावा, शास्त्रीय का एक पुनरावर्ती संस्करण for(int i=0...:

void visit(List<E> list,int pos) {
    if( pos >= list.size() ) return;
    E element= list.get(pos) ;
    ....
    visit(list,pos+1);
}

मैं उनका उल्लेख करता हूं क्योंकि आप "जावा में कुछ नए हैं" और यह दिलचस्प हो सकता है।


1
इसका उल्लेख करने के लिए धन्यवाद। मैंने कभी उपलेस्ट विधि को नहीं देखा था। हां, मैं इसे पैथोलॉजिकल मानूंगा क्योंकि मुझे किसी भी परिस्थिति का पता नहीं है, जहां शायद कभी-कभार होने वाले विरोधाभासों से अलग इसे इस्तेमाल करना फायदेमंद होगा।
IX3

3
ठीक। मुझे "पैथोलॉजिकल" कहा जाना पसंद नहीं है इसलिए यहाँ एक स्पष्टीकरण दिया गया है: मैंने पेड़ों में ट्रैकिंग या अनुसरण करते समय उनका उपयोग किया है। एक और? जावा में कुछ लिस्प कार्यक्रमों का अनुवाद करते समय मैं नहीं चाहता था कि वे अपनी लिस्प आत्मा को खो दें, और ऐसा ही किया। यदि आपको लगता है कि ये मान्य, गैर-पैथोलॉजिकल उपयोग हैं, तो टिप्पणी को बढ़ावा दें। मुझे एक ग्रुप हग चाहिए !!! :-)
मारियो रॉसी

सूची से अलग पेड़ों में रास्ते नहीं हैं? BTW, मैं आपको या किसी भी व्यक्ति को "रोगविज्ञानी" कहने का मतलब नहीं था मेरा मतलब है कि मेरे प्रश्न के कुछ उत्तर अच्छे सॉफ्टवेयर इंजीनियरिंग अभ्यास में मूल्य के लिए अव्यावहारिक या बहुत संभावना नहीं होंगे।
IX3

@ iX3 चिंता मत करो; मैं तो बस मजाक कर रहा था। पथ सूचियां हैं: "रूट पर शुरू करें" (स्पष्ट), "2 वें बच्चे की ओर बढ़ें", "1 बच्चे की ओर बढ़ें", "4 वें बच्चे की ओर जाएँ"। "रुकें"। या शॉर्ट के लिए [2,1,4]। यह एक सूची है।
मारियो रॉसी

मैं सूची की एक प्रति बनाना और उपयोग करना पसंद करूंगा while(!copyList.isEmpty()){ E e = copyList.remove(0); ... }। यह पहले संस्करण की तुलना में अधिक प्रभावी है;)।
एक्सलएच


0

एक पिछड़ी खोज के लिए आपको निम्नलिखित का उपयोग करना चाहिए:

for (ListIterator<SomeClass> iterator = list.listIterator(list.size()); iterator.hasPrevious();) {
    SomeClass item = iterator.previous();
    ...
    item.remove(); // For instance.
}

यदि आप एक स्थिति जानना चाहते हैं, तो iterator.prepretIndex () का उपयोग करें। यह एक आंतरिक लूप लिखने में भी मदद करता है जो सूची में दो पदों की तुलना करता है (पुनरावृत्तियां बराबर नहीं हैं)।


0

सही, कई विकल्प सूचीबद्ध हैं। सबसे आसान और साफ-सुथरा सिर्फ forनीचे दिए गए संवर्धित कथन का उपयोग करना होगा । Expressionकुछ प्रकार iterable है कि की है।

for ( FormalParameter : Expression ) Statement

उदाहरण के लिए, के माध्यम से पुनरावृति करने के लिए, सूची <स्ट्रिंग> आईडी, हम बस इतना कर सकते हैं,

for (String str : ids) {
    // Do something
}

3
वह पूछ रहा है कि क्या सवाल पर वर्णित लोगों के अलावा कोई और तरीका है (जिसमें यह उल्लेख किया गया है)!
एरिकाबिन

0

में java 8आप उपयोग कर सकते हैं List.forEach()के साथ विधि lambda expressionएक सूची पर पुनरावृति करने।

import java.util.ArrayList;
import java.util.List;

public class TestA {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("Apple");
        list.add("Orange");
        list.add("Banana");
        list.forEach(
                (name) -> {
                    System.out.println(name);
                }
        );
    }
}

ठंडा। आप समझा सकते हैं कि कैसे इस से अलग है eugene82के जवाब और i_am_zeroके जवाब ?
IX3

@ iX3 आह। पूरी उत्तर सूची नहीं पढ़ी। मददगार बनने की कोशिश कर रहा था .. क्या आप मुझे जवाब निकालना चाहते हैं?
खोज परिणाम वेब परिणाम Pi

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

-2

आप हमेशा पहले और तीसरे उदाहरण को थोड़ी देर के लूप और थोड़ा और कोड के साथ बदल सकते हैं। यह आपको समय का उपयोग करने में सक्षम होने का लाभ देता है:

int i = 0;
do{
 E element = list.get(i);
 i++;
}
while (i < list.size());

बेशक, इस तरह की बात के कारण NullPointerException हो सकती है यदि list.size () रिटर्न 0 है, तो हमेशा इसे कम से कम एक बार निष्पादित किया जाता है। यह परीक्षण द्वारा तय किया जा सकता है यदि तत्व अपनी विशेषताओं / विधियों का उपयोग करने से पहले शून्य है। फिर भी, लूप के लिए उपयोग करना बहुत सरल और आसान है


यह सच है ... मुझे लगता है कि तकनीकी रूप से अलग है, लेकिन व्यवहार केवल अलग है अगर सूची खाली है, है ना?
IX3

@ ix3 हां, और उस मामले में, व्यवहार बदतर है। मैं इसे एक अच्छा विकल्प नहीं कहूंगा।
CPerkins

ज़रूर, लेकिन निष्पक्षता में, यह सवाल "अच्छा" के बारे में कम है और "संभव" के बारे में अधिक है, इसलिए मैं अभी भी इस उत्तर की सराहना करता हूं।
IX3
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.