जावा 8 लैम्ब्डा मिलता है और सूची से तत्व निकालते हैं


91

तत्वों की सूची को देखते हुए, मैं किसी दिए गए संपत्ति के साथ तत्व को प्राप्त करना चाहता हूं और इसे सूची से हटा देना चाहता हूं । सबसे अच्छा समाधान मैंने पाया है:

ProducerDTO p = producersProcedureActive
                .stream()
                .filter(producer -> producer.getPod().equals(pod))
                .findFirst()
                .get();
producersProcedureActive.remove(p);

क्या एक लंबोदर अभिव्यक्ति में संयोजन और निकालना संभव है?


9
यह वास्तव में एक क्लासिक मामले की तरह लगता है जब इसके बजाय सिर्फ एक लूप और इटरेटर का उपयोग करना होता है।
चिरलीस -कुट्योप्टोफिमिस्टिक-

1
@chrylis मैं विनम्रतापूर्वक असहमत हूं;) हम अत्यावश्यक प्रोग्रामिंग के अभ्यस्त हैं, कि कोई भी अन्य तरीका बहुत ही आकर्षक लगता है। कल्पना कीजिए कि क्या वास्तविकता अन्य तरह से गोल थी: हम कार्यात्मक प्रोग्रामिंग के लिए उपयोग किए जाते हैं और जावा में एक नया अनिवार्य प्रतिमान जोड़ा जाता है। क्या आप कहेंगे कि धाराओं, विधेय और वैकल्पिक के लिए यह क्लासिक मामला होगा?
एफपीएस

9
get()यहाँ मत बुलाओ ! आपको कोई अंदाजा नहीं है कि इसकी खाली जगह है या नहीं। यदि तत्व नहीं था, तो आप एक अपवाद फेंक देंगे। इसके बजाय, ifPresent, orElse, orElseGet, या orElseThrow जैसे सुरक्षित तरीकों में से एक का उपयोग करें।
ब्रायन गोएट्ज़

@FedericoPeraltaSchaffner शायद स्वाद का मामला है। लेकिन मैं दोनों को मिलाने का प्रस्ताव भी रखूंगा। जब मैं धाराओं का उपयोग करते हुए कुछ कोड प्राप्त करता हूं तो मैं आमतौर पर मान लेता हूं कि स्ट्रीम में ऑपरेशन साइड-इफेक्ट से मुक्त हैं। तत्व को हटाने में मिश्रण के परिणामस्वरूप कोड हो सकता है जो पाठकों के लिए भ्रामक हो सकता है।
मथायस विमर

बस स्पष्ट करने के लिए: क्या आप उन सभी तत्वों को हटाना चाहते हैं listजिनके लिए Predicateसत्य है या केवल पहला (संभवतः शून्य, एक या कई तत्वों में से)?
केदार म्हसवडे

जवाबों:


154

सूची से तत्व निकालने के लिए

objectA.removeIf(x -> conditions);

उदाहरण के लिए:

objectA.removeIf(x -> blockedWorkerIds.contains(x));

List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");

List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");

str1.removeIf(x -> str2.contains(x)); 

str1.forEach(System.out::println);

OUTPUT: ए बी सी


मैं इसे सबसे अच्छा जवाब के रूप में
सुझाऊंगा

1
यह बहुत साफ-सुथरा है। यदि आपकी अपनी वस्तुओं के लिए ऐसा नहीं किया गया है तो आपको बराबर / हैशकोड लागू करने की आवश्यकता हो सकती है। (इस उदाहरण में स्ट्रिंग का उपयोग किया जाता है जो उन्हें डिफ़ॉल्ट रूप से होता है)।
bram000

17
IMHO जवाब "प्राप्त" भाग पर विचार नहीं करता है: removeIfएक संग्रह से तत्वों को हटाने के लिए एक सुरुचिपूर्ण समाधान है, लेकिन यह हटाए गए तत्व को वापस नहीं करता है।
मार्को स्ट्रैमज़ी 6

जावा ArrayList से एक वस्तु को बाहर करने के लिए एक बहुत ही सरल मोड। बहुत कुछ। मेरे लिए ठीक काम करो।
मार्सेलो रेबुकास

3
इस सवाल का जवाब नहीं है। आवश्यकता सूची से तत्व को हटाने और एक नई सूची में हटाए गए आइटम / आइटम प्राप्त करने की है।
शनिका एडिरीवेरा

35

यद्यपि धागा काफी पुराना है, फिर भी समाधान प्रदान करने के लिए सोचा - उपयोग करना Java8

removeIfफ़ंक्शन का उपयोग करें । समय जटिलता हैO(n)

producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));

एपीआई संदर्भ: RemoveIf डॉक्स

धारणा: producersProcedureActiveएक हैList

नोट: इस दृष्टिकोण के साथ आप हटाए गए आइटम की पकड़ पाने में सक्षम नहीं होंगे।


इसके अलावा तत्व को सूची से हटाने के लिए, OP अभी भी तत्व का संदर्भ चाहता है।
eee 12

@eee: यह इंगित करने के लिए बहुत बहुत धन्यवाद। मुझे ओपी के मूल प्रश्न से वह भाग छूट गया।
asifsid88

बस यह नोट करने के लिए कि शर्त से मेल खाने वाले सभी आइटम को हटा दिया जाएगा। लेकिन ओपी को लगता है कि केवल पहले आइटम को हटाने की जरूरत है (ओपी ने findFirst () का इस्तेमाल किया)
nantitv

18

कार्य करने के लिए वेनिला जावा पुनरावृत्तियों का उपयोग करने पर विचार करें:

public static <T> T findAndRemoveFirst(Iterable<? extends T> collection, Predicate<? super T> test) {
    T value = null;
    for (Iterator<? extends T> it = collection.iterator(); it.hasNext();)
        if (test.test(value = it.next())) {
            it.remove();
            return value;
        }
    return null;
}

लाभ :

  1. यह सादा और स्पष्ट है।
  2. यह केवल एक बार और केवल मिलान तत्व तक ट्रेस होता है।
  3. आप इसे Iterableबिना किसी stream()सहारे के भी कर सकते हैं (कम से कम उन remove()पर लागू करने वाले )

नुकसान :

  1. आप इसे एक एकल अभिव्यक्ति (सहायक विधि या चर की आवश्यकता) के रूप में नहीं कर सकते

से संबंधित

क्या एक लंबोदर अभिव्यक्ति में संयोजन और निकालना संभव है?

अन्य उत्तर स्पष्ट रूप से दिखाते हैं कि यह संभव है, लेकिन आपको इसके बारे में पता होना चाहिए

  1. खोज और निष्कासन सूची को दो बार पार कर सकते हैं
  2. ConcurrentModificationException सूची से तत्व निकालते समय इसे फेंका जा सकता है

5
मुझे यह समाधान पसंद है, लेकिन ध्यान दें कि यह एक गंभीर नुकसान है जो आप चूक गए थे: कई Iterable कार्यान्वयन में ऐसे remove()तरीके हैं जो UOE को फेंकते हैं। (जेडीके संग्रहों के लिए नहीं, बेशक, लेकिन मुझे लगता है कि "किसी भी शब्द पर काम करता है" कहना अनुचित है।)
ब्रायन गोएट्ज़

मुझे लगता है कि हम यह मान सकते हैं कि अगर किसी तत्व को सामान्य रूप से हटाया जा सकता है , तो इसे
पुनरावृत्त

5
आप ऐसा मान सकते हैं, लेकिन इटरेटर कार्यान्वयन के सौ को देखते हुए, यह एक बुरा अनुमान होगा। (मैं अभी भी दृष्टिकोण को पसंद करता हूं; आप बस इसे बेच रहे हैं।)
ब्रायन गोएट्ज

2
@ ब्रायन गोएट्ज़: का defaultकार्यान्वयन removeIfसमान धारणा बनाता है, लेकिन, निश्चित रूप से, यह इसके Collectionबजाय Iterable... पर परिभाषित किया गया है
होल्गर

14

प्रत्यक्ष समाधान ifPresent(consumer)द्वारा लौटाए गए वैकल्पिक पर आह्वान किया जाएगा findFirst()। वैकल्पिक खाली नहीं होने पर इस उपभोक्ता का चालान किया जाएगा। लाभ यह भी है कि यह एक अपवाद नहीं फेंकेगा यदि खोज ऑपरेशन एक खाली वैकल्पिक लौटा, जैसे आपका वर्तमान कोड करेगा; इसके बजाय, कुछ नहीं होगा।

यदि आपने निकाले गए मान देने के लिए चाहते हैं, तो आप कर सकते हैं बुला के परिणाम के :mapOptionalremove

producersProcedureActive.stream()
                        .filter(producer -> producer.getPod().equals(pod))
                        .findFirst()
                        .map(p -> {
                            producersProcedureActive.remove(p);
                            return p;
                        });

लेकिन ध्यान दें कि remove(Object)ऑपरेशन फिर से सूची को निकालने के लिए तत्व को खोजने के लिए आगे बढ़ेगा। यदि आपके पास रैंडम एक्सेस के साथ एक सूची है, जैसे ArrayList, यह सूची के अनुक्रमित पर एक स्ट्रीम बनाने के लिए बेहतर होगा और विधेय से मेल खाने वाले पहले सूचकांक का पता लगाएं:

IntStream.range(0, producersProcedureActive.size())
         .filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
         .boxed()
         .findFirst()
         .map(i -> producersProcedureActive.remove((int) i));

इस समाधान के साथ, remove(int)ऑपरेशन सीधे सूचकांक पर संचालित होता है।


3
यह एक लिंक्ड सूची के लिए पैथोलॉजिकल है।
क्राइसिस -कुट्योपोटेमिमिस्टिक-

1
@chrylis सूचकांक समाधान वास्तव में होगा। सूची के कार्यान्वयन के आधार पर, एक दूसरे के ऊपर पसंद करेगा। एक छोटा सा संपादन किया।
तुनकी

1
@chrylis: आप के मामले में LinkedListशायद धारा एपीआई का उपयोग नहीं करना चाहिए क्योंकि कम से कम दो बार ट्रैवर्स किए बिना कोई समाधान नहीं है। लेकिन मैं किसी भी वास्तविक जीवन परिदृश्य के बारे में नहीं जानता, जहां एक लिंक की गई सूची का अकादमिक लाभ इसके वास्तविक ओवरहेड की भरपाई कर सकता है। तो सरल उपाय है कि कभी उपयोग न करें LinkedList
होल्गर

2
ओह इतने सारे संपादन ... अब पहला समाधान हटाए गए तत्व को प्रदान नहीं करता है क्योंकि remove(Object)केवल booleanयह बताता है कि हटाने के लिए कोई तत्व था या नहीं।
होल्गर

3
@Marco Stramezzi: दुर्भाग्य से, यह समझाने वाली टिप्पणी हटा दी गई। boxed()तुम्हारे बिना OptionalIntजो केवल mapसे प्राप्त कर सकता intहै int। भिन्नIntStream , कोई mapToObjविधि नहीं है। के साथ boxed(), आपको एक मनमानी वस्तु प्राप्त Optional<Integer>करने की अनुमति मिलती है map, अर्थात ProducerDTOद्वारा लौटाया जाता है remove(int)। से कलाकारों Integerको intको स्पष्ट करने के लिए आवश्यक है remove(int)और remove(Object)
होल्गर

9

यदि आप पुरानी सूची को बदलना नहीं चाहते हैं, तो जावा 8 के फिल्टर का उपयोग कर सकते हैं, और दूसरी सूची बना सकते हैं:

List<ProducerDTO> result = producersProcedureActive
                            .stream()
                            .filter(producer -> producer.getPod().equals(pod))
                            .collect(Collectors.toList());

5

मुझे यकीन है कि यह एक अलोकप्रिय जवाब होगा, लेकिन यह काम करता है ...

ProducerDTO[] p = new ProducerDTO[1];
producersProcedureActive
            .stream()
            .filter(producer -> producer.getPod().equals(pod))
            .findFirst()
            .ifPresent(producer -> {producersProcedureActive.remove(producer); p[0] = producer;}

p[0] या तो पाया गया तत्व धारण करेगा या अशक्त होगा।

यहां "ट्रिक" एक सरणी संदर्भ का उपयोग करके "प्रभावी ढंग से अंतिम" समस्या को दरकिनार कर रहा है जो प्रभावी रूप से अंतिम है, लेकिन इसके पहले तत्व को सेट कर रहा है।


1
यह मामला, यह इतना बुरा नहीं है, लेकिन सिर्फ .orElse(null)पाने के लिए कॉल करने की संभावना पर एक सुधार नहीं है ProducerDTOया null...
होल्गर

उस मामले में, यह आसान हो सकता है बस .orElse(null)और एक है if, नहीं?
तुनकी

@ होलगर लेकिन आप कैसे remove()भी उपयोग करके आह्वान कर सकते हैं orElse(null)?
बोहेमियन

1
बस परिणाम का उपयोग करें। if(p!=null) producersProcedureActive.remove(p);यह आपके ifPresentकॉल में लंबोदर अभिव्यक्ति से अभी भी कम है ।
होल्गर

एक 1-लाइन समाधान यानी - @holger मैं परहेज एकाधिक बयान होने के रूप में प्रश्न के लक्ष्य व्याख्या
बोहेमियन

4

साथ ग्रहण संग्रह आप उपयोग कर सकते हैं detectIndexके साथ remove(int)किसी भी java.util.List पर।

List<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = Iterate.detectIndex(integers, i -> i > 2);
if (index > -1) {
    integers.remove(index);
}

Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);

यदि आप MutableListग्रहण संग्रह से प्रकार का उपयोग करते हैं , तो आप detectIndexविधि को सीधे सूची में कॉल कर सकते हैं ।

MutableList<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5);
int index = integers.detectIndex(i -> i > 2);
if (index > -1) {
    integers.remove(index);
}

Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);

नोट: मैं ग्रहण संग्रहों के लिए एक कमिटेटर हूं


2

जब हम एक सूची से एक नई सूची (एक विधेय का उपयोग करके फ़िल्टर) में कई तत्वों को प्राप्त करना चाहते हैं और उन्हें मौजूदा सूची से हटा देते हैं , तो मुझे कहीं भी उचित उत्तर नहीं मिला।

यहाँ हम जावा स्ट्रीमिंग एपीआई विभाजन का उपयोग करके इसे कैसे कर सकते हैं।

Map<Boolean, List<ProducerDTO>> classifiedElements = producersProcedureActive
    .stream()
    .collect(Collectors.partitioningBy(producer -> producer.getPod().equals(pod)));

// get two new lists 
List<ProducerDTO> matching = classifiedElements.get(true);
List<ProducerDTO> nonMatching = classifiedElements.get(false);

// OR get non-matching elements to the existing list
producersProcedureActive = classifiedElements.get(false);

इस तरह से आप फ़िल्टर किए गए तत्वों को मूल सूची से प्रभावी रूप से हटा देते हैं और उन्हें एक नई सूची में जोड़ देते हैं।

5.2 का संदर्भ लें इस आलेख के संग्राहक .partingBy खंड ।


1

जैसा कि दूसरों ने सुझाव दिया है, यह छोरों और पुनरावृत्तियों के लिए उपयोग का मामला हो सकता है। मेरी राय में, यह सबसे सरल तरीका है। यदि आप सूची को जगह में बदलना चाहते हैं, तो इसे वैसे भी "वास्तविक" कार्यात्मक प्रोग्रामिंग नहीं माना जा सकता है। लेकिन आप उन Collectors.partitioningBy()तत्वों के साथ एक नई सूची प्राप्त करने के लिए उपयोग कर सकते हैं जो आपकी स्थिति को संतुष्ट करते हैं, और उन लोगों की एक नई सूची जो नहीं करते हैं। बेशक इस दृष्टिकोण के साथ, यदि आपके पास कई तत्व हैं जो स्थिति को संतुष्ट करते हैं, तो वे सभी उस सूची में होंगे और न केवल पहले।


स्ट्रीम को फ़िल्टर करने और नई सूची में परिणाम एकत्र करने के लिए बहुत बेहतर है
एफपीएस

1

मूल सूची को संशोधित किए बिना निम्न तर्क समाधान है

List<String> str1 = new ArrayList<String>();
str1.add("A");
str1.add("B");
str1.add("C");
str1.add("D");

List<String> str2 = new ArrayList<String>();
str2.add("D");
str2.add("E");

List<String> str3 = str1.stream()
                        .filter(item -> !str2.contains(item))
                        .collect(Collectors.toList());

str1 // ["A", "B", "C", "D"]
str2 // ["D", "E"]
str3 // ["A", "B", "C"]

0

मेरे प्रारंभिक विचार और आपके उत्तरों को मिलाकर मैं अपने स्वयं के प्रश्न का हल ढूंढता हूं:

public ProducerDTO findAndRemove(String pod) {
    ProducerDTO p = null;
    try {
        p = IntStream.range(0, producersProcedureActive.size())
             .filter(i -> producersProcedureActive.get(i).getPod().equals(pod))
             .boxed()
             .findFirst()
             .map(i -> producersProcedureActive.remove((int)i))
             .get();
        logger.debug(p);
    } catch (NoSuchElementException e) {
        logger.error("No producer found with POD [" + pod + "]");
    }
    return p;
}

यह ऑब्जेक्ट का उपयोग करके उसे remove(int)फिर से सूची (@Tunaki द्वारा सुझाए गए) के रूप में नहीं निकालता है और यह फ़ंक्शन कॉलर को हटाए गए ऑब्जेक्ट को वापस करने देता है।

मैंने आपके उत्तर पढ़े जो मुझे ifPresentइसके बजाय सुरक्षित तरीके चुनने का सुझाव देते हैंget लेकिन मुझे इस परिदृश्य में उनका उपयोग करने का कोई तरीका नहीं मिला।

क्या इस तरह के समाधान में कोई महत्वपूर्ण कमी है?

@Holger सलाह के बाद संपादित करें

यह वह फ़ंक्शन होना चाहिए जिसकी मुझे आवश्यकता थी

public ProducerDTO findAndRemove(String pod) {
    return IntStream.range(0, producersProcedureActive.size())
            .filter(i -> producersProcedureActive.get(i).getPod().equals(pod))      
            .boxed()                                                                
            .findFirst()
            .map(i -> producersProcedureActive.remove((int)i))
            .orElseGet(() -> {
                logger.error("No producer found with POD [" + pod + "]"); 
                return null; 
            });
}

2
आपको getअपवाद का उपयोग और पकड़ नहीं करना चाहिए । यह न केवल खराब शैली है, बल्कि खराब प्रदर्शन भी हो सकता है। साफ समाधान और भी सरल है,return /* stream operation*/.findFirst() .map(i -> producersProcedureActive.remove((int)i)) .orElseGet(() -> { logger.error("No producer found with POD [" + pod + "]"); return null; });
Holger

0

कार्य यह है: सूची से तत्व ✶ और is निकालें

p.stream().collect( Collectors.collectingAndThen( Collector.of(
    ArrayDeque::new,
    (a, producer) -> {
      if( producer.getPod().equals( pod ) )
        a.addLast( producer );
    },
    (a1, a2) -> {
      return( a1 );
    },
    rslt -> rslt.pollFirst()
  ),
  (e) -> {
    if( e != null )
      p.remove( e );  // remove
    return( e );    // get
  } ) );
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.