Java List.contains (x के बराबर फ़ील्ड मान वाली वस्तु)


199

मैं यह जांचना चाहता हूं कि क्या कोई Listऐसा ऑब्जेक्ट है जिसमें एक निश्चित मान के साथ एक फ़ील्ड है। अब, मैं एक लूप का उपयोग कर सकता हूं और जांच कर सकता हूं, लेकिन अगर कुछ और कोड कुशल था तो मैं उत्सुक था।

कुछ इस तरह;

if(list.contains(new Object().setName("John"))){
    //Do some stuff
}

मुझे पता है कि उपरोक्त कोड कुछ भी नहीं करता है, यह केवल मोटे तौर पर प्रदर्शित करने के लिए है कि मैं क्या हासिल करने की कोशिश कर रहा हूं।

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


5
चूंकि यह कस्टम समानता है, इसलिए आपको कस्टम कोड लिखना होगा।
सोतिरियोस डेलिमोलिसिप

आपका घोषित लक्ष्य और आपका कोड उदाहरण मेल नहीं खाते। क्या आप केवल एक फ़ील्ड मान के आधार पर वस्तुओं की तुलना करना चाहते हैं?
डंकन जोन्स

1
equals(Object)अपने कस्टम ऑब्जेक्ट की विधि को ओवरराइड करें ?
जोश एम।

1
for(Person p:list) if (p.getName().equals("John") return true; return false;मुझे डर है कि जावा में आपको अधिक संक्षिप्त तरीका नहीं मिलेगा।
माइकफा १।

@ राजदीप क्षमा करें, मुझे आपका प्रश्न समझ में नहीं आया। हमेशा सच p.equals(p) होना चाहिए , इसलिए मैं उलझन में हूं कि आप क्या हासिल करने की कोशिश कर रहे हैं। उम्मीद है कि अगर आप कोई नया सवाल पूछेंगे तो आपको बेहतर मदद मिल सकती है।
16

जवाबों:


267

स्ट्रीम

यदि आप जावा 8 का उपयोग कर रहे हैं, तो शायद आप कुछ इस तरह की कोशिश कर सकते हैं:

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().filter(o -> o.getName().equals(name)).findFirst().isPresent();
}

या वैकल्पिक रूप से, आप कुछ इस तरह की कोशिश कर सकते हैं:

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().map(MyObject::getName).filter(name::equals).findFirst().isPresent();
}

इस विधि वापस आ जाएगी trueअगर List<MyObject>एक होता है MyObjectनाम के साथ name। आप में से प्रत्येक पर कार्रवाई चाहते हैं MyObjectकि getName().equals(name), तो आप कुछ इस तरह की कोशिश कर सकते:

public void perform(final List<MyObject> list, final String name){
    list.stream().filter(o -> o.getName().equals(name)).forEach(
            o -> {
                //...
            }
    );
}

जहां oएक MyObjectउदाहरण प्रस्तुत करता है ।

वैकल्पिक रूप से, टिप्पणियों के अनुसार सुझाव (धन्यवाद MK10), आप Stream#anyMatchविधि का उपयोग कर सकते हैं :

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().anyMatch(o -> o.getName().equals(name));
}

1
फिर, दूसरा उदाहरण होना चाहिए public boolean। इसके अलावा, आप Objects.equals()मामले में उपयोग करना चाहते o.getName()हो सकता है null
एरिक जबलो

1
मैं सिर्फ एक पैरानॉयड प्रोग्रामर हूं। मैंने उन परियोजनाओं से निपटा है जहां लोग तेज और अशक्त थे, इसलिए मैं रक्षात्मक हो गया। यह सुनिश्चित करने के लिए बेहतर है कि आइटम कभी भी अशक्त न हों।
एरिक जबलो

5
@EricJablow शायद आपको उन सभी उत्तरों पर टिप्पणी करनी चाहिए जो nullचेक नहीं करते हैं , जैसा कि सिर्फ एक (मेरा) के विपरीत है।
जोश एम।

55
और भी छोटा और समझने में आसान: return list.stream().anyMatch(o -> o.getName().equals(name));
MK10

3
मैं @ MK10 से java.util.Objectsसहमत हूं लेकिन मैं nullsafe की तुलना के लिए उपयोग करूंगा। return list.stream().anyMatch(o -> Objects.euqals(o.getName(), name);यदि आप null के लिए स्ट्रीम में ऑब्जेक्ट्स चेक करना चाहते हैं, तो आप .filter(Objects::nonNull)anyMatch से पहले जोड़ सकते हैं ।
तोबजदक

81

आपके पास दो विकल्प हैं।

1. पहली पसंद, जो बेहतर है, आपके ऑब्जेक्ट क्लास में `बराबर ()` विधि को ओवरराइड करना है।

उदाहरण के लिए, मान लें कि आपके पास यह ऑब्जेक्ट क्लास है:

public class MyObject {
    private String name;
    private String location;
    //getters and setters
}

अब हम कहते हैं कि आप केवल MyObject के नाम के बारे में परवाह करते हैं, कि यह अद्वितीय होना चाहिए, यदि दो `MyObject` का एक ही नाम हो तो उन्हें समान माना जाना चाहिए। उस स्थिति में, आप `बराबर ()` विधि (और `हैशकोड ()` विधि) को भी ओवरराइड करना चाहेंगे ताकि यह समानता निर्धारित करने के लिए नामों की तुलना करे।

एक बार जब आप ऐसा कर लेते हैं, तो आप यह देख सकते हैं कि क्या किसी संग्रह में "फू" नाम के साथ MyObject शामिल है, जैसे:

MyObject object = new MyObject();
object.setName("foo");
collection.contains(object);

हालाँकि, यह आपके लिए एक विकल्प नहीं हो सकता है:

  • आप समानता के लिए जाँच करने के लिए नाम और स्थान दोनों का उपयोग कर रहे हैं, लेकिन आप केवल यह जाँचना चाहते हैं कि किसी संग्रह में किसी निश्चित स्थान के साथ कोई `MyObject` तो नहीं है। इस मामले में, आप पहले से ही 'बराबर' () `ओवरराइड कर चुके हैं।
  • `MyObject` एक एपीआई का हिस्सा है जिसे बदलने के लिए आपके पास स्वतंत्रता नहीं है।

यदि इनमें से कोई भी मामला है, तो आप विकल्प 2 चाहेंगे:

2. अपनी उपयोगिता विधि लिखें:

public static boolean containsLocation(Collection<MyObject> c, String location) {
    for(MyObject o : c) {
        if(o != null && o.getLocation.equals(location)) {
            return true;
        }
    }
    return false;
}

वैकल्पिक रूप से, आप ArrayList (या कुछ अन्य संग्रह) का विस्तार कर सकते हैं और फिर उसमें अपनी खुद की विधि जोड़ सकते हैं:

public boolean containsLocation(String location) {
    for(MyObject o : this) {
        if(o != null && o.getLocation.equals(location)) {
                return true;
            }
        }
        return false;
    }

दुर्भाग्य से इसके आसपास एक बेहतर तरीका नहीं है।


मुझे लगता है कि आप अगर (o! Null && o.getLocation.equals (स्थान) में गेट्टर के लिए कोष्ठक भूल गए)
olszi

25

Google अमरूद

यदि आप अमरूद का उपयोग कर रहे हैं , तो आप एक कार्यात्मक तरीका अपना सकते हैं और निम्नलिखित कार्य कर सकते हैं

FluentIterable.from(list).find(new Predicate<MyObject>() {
   public boolean apply(MyObject input) {
      return "John".equals(input.getName());
   }
}).Any();

जो थोड़ा वर्बोज़ दिखता है। हालाँकि विधेय एक वस्तु है और आप अलग-अलग खोजों के लिए विभिन्न प्रकार प्रदान कर सकते हैं। ध्यान दें कि कैसे पुस्तकालय ही संग्रह की पुनरावृत्ति और उस फ़ंक्शन को अलग करता है जिसे आप लागू करना चाहते हैं। आपको equals()किसी विशेष व्यवहार के लिए ओवरराइड नहीं करना है ।

जैसा कि नीचे उल्लेख किया गया है, जावा 8 में निर्मित java.util.Stream फ्रेमवर्क और बाद में कुछ इसी तरह प्रदान करता है।


1
वैकल्पिक रूप से आप उपयोग कर सकते हैं Iterables.any(list, predicate), जो व्यावहारिक रूप से समान है, और आप इसे स्टाइलिस्टिक रूप से पसंद कर सकते हैं या नहीं।
मीकफ़े

@ एरिक जाब्लो यप। :) आप मेरे समाधान को देख सकते हैं।
जोश एम।

25

यह जावा 8+ का उपयोग करके इसे कैसे करना है:

boolean isJohnAlive = list.stream().anyMatch(o -> o.getName().equals("John"));

20

Collection.contains()equals()प्रत्येक वस्तु को एक रिटर्न पर कॉल करके लागू किया जाता है true

तो इसे लागू करने का एक तरीका ओवरराइड करना है equals()लेकिन निश्चित रूप से, आपके पास केवल एक ही हो सकता है।

अमरूद जैसे ढांचे इसके लिए विधेय का उपयोग करते हैं। इसके साथ Iterables.find(list, predicate), आप परीक्षण को विधेय में डालकर मनमाने क्षेत्रों की खोज कर सकते हैं।

VM के शीर्ष पर निर्मित अन्य भाषाओं में इसे बनाया गया है। Groovy में , उदाहरण के लिए, आप बस लिखते हैं:

def result = list.find{ it.name == 'John' }

जावा 8 ने हमारे सभी जीवन को भी आसान बना दिया है:

List<Foo> result = list.stream()
    .filter(it -> "John".equals(it.getName())
    .collect(Collectors.toList());

यदि आप इस तरह की चीजों की परवाह करते हैं, तो मैं "बियॉन्ड जावा" पुस्तक का सुझाव देता हूं। इसमें जावा की कई कमियों और अन्य भाषाओं के बेहतर होने के कई उदाहरण हैं।


इसलिए, अगर मैं सूची में जोड़े जा रहे कस्टम ऑब्जेक्ट्स के बराबर विधि को ओवरराइड करता हूं ... तो क्या मैं इसे सही रिटर्न के लिए प्राप्त कर सकता हूं यदि कुछ वर्ग चर समान हैं?
रूडी करशॉ

1
नहीं, पीछे विचार equals()बहुत सीमित है। इसका अर्थ है "वस्तु पहचान" की जांच करना - जो कुछ भी वस्तुओं के कुछ वर्ग के लिए हो सकता है। यदि आपने एक ध्वज जोड़ा है जिसमें फ़ील्ड शामिल किए जाने चाहिए, तो यह सभी प्रकार की परेशानी का कारण बन सकता है। मैं इसके खिलाफ दृढ़ता से सलाह देता हूं।
आरोन दिगुल्ला

प्यार उमंग है, तो "नया" जावा 8 लैम्ब्डा वास्तव में हमें यह शीतलता अभी भी नहीं देता है? def result = list.find{ it.name == 'John' }प्रोग्रामर के खिलाफ युद्ध अपराधों के लिए ओरेकल / जेसीपी पर मुकदमा दायर किया जाना चाहिए।
केन

19

द्विआधारी खोज

अपनी सूची में एक तत्व खोजने के लिए आप संग्रह का उपयोग कर सकते हैं । सूची को मान लिया गया है)

Collections.binarySearch(list, new YourObject("a1", "b",
                "c"), new Comparator<YourObject>() {

            @Override
            public int compare(YourObject o1, YourObject o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });

यदि वस्तु संग्रह में मौजूद नहीं है तो एक ऋणात्मक संख्या वापस आ जाएगी या फिर वह indexवस्तु का वापस कर देगी । इसके साथ आप अलग-अलग खोज रणनीतियों वाली वस्तुओं की खोज कर सकते हैं।


यह छोटी परियोजनाओं के लिए एक बढ़िया समाधान है जो अमरूद का उपयोग नहीं करना चाहते हैं। एक सवाल हालांकि, द्विआधारी खोज पर डॉक्स बताता है कि: "इस कॉल को करने से पहले, सूची को निर्दिष्ट तुलनित्र के अनुसार आरोही क्रम में क्रमबद्ध किया जाना चाहिए। यदि इसे सॉर्ट नहीं किया गया है, तो परिणाम अपरिभाषित हैं"। लेकिन कुछ मामलों में लगता है, क्या तुलना की जा रही है पर निर्भर करता है, यह मामला नहीं हो सकता है?
क्रिस्मस सक्सेज

और यदि आप इसे जोड़ते हैं और पुन: छाँटते हैं तो यह नकारात्मक संख्या दर्शाता है कि ऑब्जेक्ट कहाँ डाला जाएगा।
fiorentinoing

8

नक्शा

आप Hashmap<String, Object>एक कुंजी के रूप में एक मान का उपयोग करके बना सकते हैं , और फिर देख सकते हैं कि क्या यह yourHashMap.keySet().contains(yourValue)सच है।


+1 हालांकि इसका अर्थ है कि आप पहले स्थान पर नक्शे में विवरणों को डालने के बाद थोड़ा ओवरहेड हो जाते हैं। मुझे आश्चर्य है कि अब तक किसी ने भी इसका सुझाव नहीं दिया है।
रूडी करशॉ

6

ग्रहण संग्रह

यदि आप ग्रहण संग्रह का उपयोग कर रहे हैं , तो आप anySatisfy()विधि का उपयोग कर सकते हैं । या तो Listएक में अपने को अनुकूलित करें ListAdapterया यदि संभव हो तो अपने Listको बदलें ListIterable

ListIterable<MyObject> list = ...;

boolean result =
    list.anySatisfy(myObject -> myObject.getName().equals("John"));

यदि आप बार-बार इस तरह के ऑपरेशन करते हैं, तो यह एक तरीका निकालना बेहतर होता है जो उत्तर देता है कि क्या प्रकार की विशेषता है।

public class MyObject
{
    private final String name;

    public MyObject(String name)
    {
        this.name = name;
    }

    public boolean named(String name)
    {
        return Objects.equals(this.name, name);
    }
}

आप anySatisfyWith()विधि संदर्भ के साथ वैकल्पिक रूप का उपयोग कर सकते हैं ।

boolean result = list.anySatisfyWith(MyObject::named, "John");

यदि आप अपने Listको इसमें नहीं बदल सकते हैं ListIterable, तो यहां बताया गया है कि आप इसका उपयोग कैसे करेंगे ListAdapter

boolean result = 
    ListAdapter.adapt(list).anySatisfyWith(MyObject::named, "John");

नोट: मैं एक्लिप्स ऑलियन्स के लिए एक कमिटेटर हूं।


5

Predicate

यदि आप Java 8, या लाइब्रेरी का उपयोग नहीं करते हैं जो आपको संग्रह से निपटने के लिए अधिक कार्यक्षमता प्रदान करता है, तो आप कुछ ऐसा लागू कर सकते हैं जो आपके समाधान के लिए अधिक पुन: प्रयोज्य हो सकता है।

interface Predicate<T>{
        boolean contains(T item);
    }

    static class CollectionUtil{

        public static <T> T find(final Collection<T> collection,final  Predicate<T> predicate){
            for (T item : collection){
                if (predicate.contains(item)){
                    return item;
                }
            }
            return null;
        }
    // and many more methods to deal with collection    
    }

मैं कुछ इस तरह का उपयोग कर रहा हूँ, मेरे पास इंटरफ़ेस है, और मैं इसे अपने उपयोग वर्ग में लागू कर रहा हूँ।

मेरे रास्ते में ऐसा करने से क्या फायदा? आपके पास एक तरीका है जो किसी भी प्रकार के संग्रह में खोज करने से संबंधित है। और यदि आपको अलग-अलग क्षेत्र द्वारा खोज करना है तो आपको अलग-अलग तरीके नहीं बनाने होंगे। आपको जो कुछ भी करने की आवश्यकता है उसे अलग-अलग विधेय प्रदान करें जो कि जल्द ही उपयोगी नहीं हो सकता है।

यदि आप इसका उपयोग करना चाहते हैं, तो आपको जो कुछ भी करने की आवश्यकता है वह कॉल विधि है और टाइयर विधेय को परिभाषित करें

CollectionUtil.find(list, new Predicate<MyObject>{
    public boolean contains(T item){
        return "John".equals(item.getName());
     }
});

4

यहाँ अमरूद का उपयोग कर एक समाधान है

private boolean checkUserListContainName(List<User> userList, final String targetName){

    return FluentIterable.from(userList).anyMatch(new Predicate<User>() {
        @Override
        public boolean apply(@Nullable User input) {
            return input.getName().equals(targetName);
        }
    });
}

3

containsविधि equalsआंतरिक रूप से उपयोग करता है । इसलिए आपको ओवरराइड करने की आवश्यकता हैequals अपनी आवश्यकता के अनुसार अपनी कक्षा के लिए विधि की आवश्यकता है।

Btw यह सिंटिकली सही नहीं लगता है:

new Object().setName("John")

3
जब तक सेटर वापस नहीं आता this
सोतिरियोस डेलिमोलिस

इसलिए, अगर मैं सूची में जोड़े जा रहे कस्टम ऑब्जेक्ट्स के बराबर विधि को ओवरराइड करता हूं ... तो क्या मैं इसे सही रिटर्न के लिए प्राप्त कर सकता हूं यदि कुछ वर्ग चर समान हैं?
रूडी करशॉ १udi

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

1
यह वास्तव में equalsएकल उपयोग के मामले में ओवरराइड करने के लिए उचित नहीं है , जब तक कि यह सामान्य रूप से वर्ग के लिए समझ में न आए। आप लूप लिखने से बहुत बेहतर होंगे।
माइकफेह सेप

@MikeFHay सहमत है, इसलिए मैंने टिप्पणी में उल्लेख किया है "तो अगर आपको लगता है कि यह तार्किक रूप से यह कहना सही है कि एक ही नाम वाली दो वस्तुएं समान वस्तुएं हैं तो वापस लौटें"
जुनैद अहसन

1

यदि आपको इसे List.contains(Object with field value equal to x)बार-बार करने की आवश्यकता है , तो एक सरल और कुशल समाधान होगा:

List<field obj type> fieldOfInterestValues = new ArrayList<field obj type>;
for(Object obj : List) {
    fieldOfInterestValues.add(obj.getFieldOfInterest());
}

फिर List.contains(Object with field value equal to x)जैसा परिणाम होगा वैसा ही होगाfieldOfInterestValues.contains(x);


1

आप भी anyMatch()उपयोग कर सकते हैं:

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().anyMatch(o -> o.getName().contains(name));
}

0

JAVA 8 SDK के बावजूद संग्रह उपकरण का एक बहुत कुछ है लाइब्रेरी आपके साथ काम करने में मदद कर सकती है, उदाहरण के लिए: http://commons.apache.org/proper/commons-collections/

Predicate condition = new Predicate() {
   boolean evaluate(Object obj) {
        return ((Sample)obj).myField.equals("myVal");
   }
};
List result = CollectionUtils.select( list, condition );
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.