जावा में धारा को संशोधित करते हुए वस्तुओं को संशोधित करना


88

Java8 स्ट्रीम में, क्या मुझे ऑब्जेक्ट्स को संशोधित / अपडेट करने की अनुमति है? उदाहरण के लिए। List<User> users:

users.stream().forEach(u -> u.setProperty("value"))

जवाबों:


102

हां, आप अपनी स्ट्रीम के अंदर वस्तुओं की स्थिति को संशोधित कर सकते हैं, लेकिन अक्सर आपको स्ट्रीम के स्रोत को संशोधित करने से बचना चाहिए । से हस्तक्षेप न करने धारा पैकेज प्रलेखन हम चाहते हैं कि पढ़ सकते हैं की धारा:

अधिकांश डेटा स्रोतों के लिए, हस्तक्षेप को रोकने का मतलब यह सुनिश्चित करना है कि स्ट्रीम पाइपलाइन के निष्पादन के दौरान डेटा स्रोत बिल्कुल भी संशोधित नहीं है । इसके उल्लेखनीय अपवाद वे धाराएँ हैं जिनके स्रोत समवर्ती संग्रह हैं, जिन्हें विशेष रूप से समवर्ती संशोधन को संभालने के लिए डिज़ाइन किया गया है। समवर्ती धारा स्रोत वे हैं जिनकी Spliteratorरिपोर्ट की CONCURRENTविशेषता है।

तो यह ठीक है

  List<User> users = getUsers();
  users.stream().forEach(u -> u.setProperty(value));
//                       ^    ^^^^^^^^^^^^^

लेकिन ज्यादातर मामलों में ऐसा नहीं है

  users.stream().forEach(u -> users.remove(u));
//^^^^^                       ^^^^^^^^^^^^

और ConcurrentModificationExceptionNPE जैसे अन्य अनपेक्षित अपवादों को भी फेंक या दे सकते हैं :

List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());

list.stream()
    .filter(i -> i > 5)
    .forEach(i -> list.remove(i));  //throws NullPointerException

4
यदि आप उपयोगकर्ताओं को संशोधित करना चाहते हैं तो समाधान क्या हैं?
अगस्त

2
@ ऑगस्टस यह सब इस बात पर निर्भर करता है कि आप इस सूची को कैसे संशोधित करना चाहते हैं। लेकिन आम तौर पर आपको इससे बचना चाहिए Iteratorजो संशोधित सूची (नए तत्वों को हटाने / जोड़ने) को रोकता है जबकि पुनरावृत्ति और दोनों धाराएँ और प्रत्येक के लिए लूप इसका उपयोग कर रहे हैं। आप साधारण लूप का उपयोग करने की कोशिश कर सकते हैं, for(int i=0; ..; ..)जिसमें यह समस्या नहीं है (लेकिन जब आपका धागा आपकी सूची को संशोधित करेगा) आपको रोक नहीं पाएगा। तुम भी तरह के तरीकों का उपयोग कर सकते list.removeAll(Collection), list.removeIf(Predicate)। इसके अलावा java.util.Collectionsकक्षा में कुछ विधियाँ होती हैं जो उपयोगी हो सकती हैं addAll(CollectionOfNewElements,list)
Pshemo

@Phemo, इसका एक समाधान यह है कि आप अपनी प्राथमिक सूची में आइटमों के साथ ArrayList जैसे संग्रह का एक नया उदाहरण बनाएं; नई सूची पर पुनरावृति करें, और प्राथमिक सूची पर ऑपरेशन करें: नया ArrayList <> (उपयोगकर्ता) .stream .forEach (u -> users.remove (u));
MNZ

2
@ बलौनिर हल क्या? हमें स्ट्रीम का उपयोग करते समय धारा के स्रोत को संशोधित करने की अनुमति नहीं है, लेकिन इसके तत्वों की स्थिति को संशोधित करने की अनुमति है। इससे कोई फर्क नहीं पड़ता कि हम मानचित्र, फ़ॉरच या अन्य स्ट्रीम विधियों का उपयोग करते हैं।
Pshemo

1
संग्रह को संशोधित करने के बजाय मैं उपयोग करने का सुझाव filter()
दूंगा

5

कार्यात्मक तरीका imho होगा:

import static java.util.stream.Collectors.toList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class PredicateTestRun {

    public static void main(String[] args) {

        List<String> lines = Arrays.asList("a", "b", "c");
        System.out.println(lines); // [a, b, c]
        Predicate<? super String> predicate = value -> "b".equals(value);
        lines = lines.stream().filter(predicate.negate()).collect(toList());

        System.out.println(lines); // [a, c]
    }
}

इस समाधान में मूल सूची को संशोधित नहीं किया गया है, लेकिन एक नई सूची में आपका अपेक्षित परिणाम शामिल होना चाहिए जो पुराने चर के समान ही चर के तहत सुलभ हो।


3

धारा के स्रोत पर संरचनात्मक संशोधन करने के लिए, जैसा कि उनके उत्तर में वर्णित शेमहो, एक समाधान है कि आप अपनी प्राथमिक सूची के अंदर वस्तुओं के साथ एक Collectionतरह का नया उदाहरण बनाएं ArrayList; नई सूची पर पुनरावृत्त करें, और प्राथमिक सूची पर संचालन करें।

new ArrayList<>(users).stream().forEach(u -> users.remove(u));

1

ConcurrentModificationException से छुटकारा पाने के लिए CopyOnWriteArrayList का उपयोग करें


2
यह वास्तव में सही है, लेकिन: यह उस विशिष्ट वर्ग पर निर्भर करता है जिसका उपयोग यहां किया जाता है। लेकिन जब आप केवल यह जानते हैं कि आपके पास एक है List<Something>- तो आपको पता नहीं है कि एक CopyOnWriteArrayList है। तो यह एक औसत दर्जे की टिप्पणी की तरह है, लेकिन एक वास्तविक जवाब नहीं है।
घोस्टकट

अगर उसने इसे एक टिप्पणी के रूप में पोस्ट किया था, तो किसी ने सबसे अधिक संभावना उसे बताई होगी कि वह टिप्पणियों के रूप में उत्तर पोस्ट न करें।
बिल के

1

अजीब चीजें बनाने के बजाय, आप बस filter()और फिर map()अपना परिणाम कर सकते हैं ।

यह बहुत अधिक पठनीय और सुनिश्चित है। धाराएँ इसे केवल एक लूप में बनाएंगी।


1

आप removeIfसशर्त रूप से सूची से डेटा निकालने के लिए उपयोग कर सकते हैं।

उदाहरण: - यदि आप किसी सूची से सभी संख्याओं को हटाना चाहते हैं, तो आप इसे निम्नानुसार कर सकते हैं।

    final List<Integer> list = IntStream.range(1,100).boxed().collect(Collectors.toList());

    list.removeIf(number -> number % 2 == 0);

1

हां, आप अपने मामले में सूची में मौजूद वस्तुओं के मूल्यों को संशोधित या अपडेट कर सकते हैं:

users.stream().forEach(u -> u.setProperty("some_value"))

हालाँकि, उपरोक्त कथन स्रोत ऑब्जेक्ट पर अद्यतन करेगा। जो ज्यादातर मामलों में स्वीकार्य नहीं हो सकता है।

सौभाग्य से, हमारे पास एक और तरीका है:

List<Users> updatedUsers = users.stream().map(u -> u.setProperty("some_value")).collect(Collectors.toList());

जो पुरानी सूची में बाधा डाले बिना एक अद्यतन सूची वापस लौटाता है।


0

जैसा कि पहले उल्लेख किया गया था - आप मूल सूची को संशोधित नहीं कर सकते, लेकिन आप नई सूची में आइटमों को स्ट्रीम, संशोधित और एकत्र कर सकते हैं। यहां सरल उदाहरण है कि स्ट्रिंग तत्व को कैसे संशोधित किया जाए।

public class StreamTest {

    @Test
    public void replaceInsideStream()  {
        List<String> list = Arrays.asList("test1", "test2_attr", "test3");
        List<String> output = list.stream().map(value -> value.replace("_attr", "")).collect(Collectors.toList());
        System.out.println("Output: " + output); // Output: [test1, test2, test3]
    }
}

-1

इसमें थोड़ी देर हो सकती है। लेकिन यहाँ एक उपयोग है। यह फाइलों की संख्या की गिनती प्राप्त करने के लिए है।

मेमोरी के लिए एक पॉइंटर बनाएं (इस मामले में एक नया ओज) और उसमें संशोधित वस्तु की संपत्ति हो। जावा 8 स्ट्रीम सूचक को स्वयं को संशोधित करने की अनुमति नहीं देता है और इसलिए यदि आप सिर्फ एक चर के रूप में गिनती की घोषणा करते हैं और धारा के भीतर वृद्धि करने की कोशिश करते हैं तो यह कभी भी काम नहीं करेगा और पहली बार में एक संकलक अपवाद को फेंक देगा।

Path path = Paths.get("/Users/XXXX/static/test.txt");



Count c = new Count();
            c.setCount(0);
            Files.lines(path).forEach(item -> {
                c.setCount(c.getCount()+1);
                System.out.println(item);});
            System.out.println("line count,"+c);

public static class Count{
        private int count;

        public int getCount() {
            return count;
        }

        public void setCount(int count) {
            this.count = count;
        }

        @Override
        public String toString() {
            return "Count [count=" + count + "]";
        }



    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.