java - जावा संग्रह फ़िल्टर करने का सबसे अच्छा तरीका क्या है?




collections filter (17)

मैं एक java.util.Collection एक predicate के आधार पर फ़िल्टर करना चाहता हूँ।


"बेस्ट" तरीका बहुत व्यापक अनुरोध है। क्या यह "सबसे छोटा" है? "सबसे तेज"? "पठनीय"? जगह में या किसी अन्य संग्रह में फ़िल्टर करें?

सरल (लेकिन सबसे अधिक पढ़ने योग्य नहीं) तरीका इसे फिर से शुरू करना और Iterator.remove () विधि का उपयोग करना है:

Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
  Foo foo = it.next();
  if( !condition(foo) ) it.remove();
}

अब, इसे और अधिक पठनीय बनाने के लिए, आप इसे उपयोगिता विधि में लपेट सकते हैं। फिर एक आईप्रेडिकेट इंटरफ़ेस का आविष्कार करें, उस इंटरफ़ेस का अनाम कार्यान्वयन बनाएं और कुछ ऐसा करें:

CollectionUtils.filterInPlace(col,
  new IPredicate<Foo>(){
    public boolean keepIt(Foo foo) {
      return foo.isBar();
    }
  });

जहां filterInPlace () संग्रह को पुन: सक्रिय करता है और संग्रह में रखे जाने वाले उदाहरण को जानने के लिए Predicate.keepIt () को कॉल करता है।

मुझे वास्तव में इस कार्य के लिए किसी तृतीय-पक्ष लाइब्रेरी को लाने के लिए औचित्य नहीं दिख रहा है।



अमरूद के साथ:

Collection<Integer> collection = Lists.newArrayList(1, 2, 3, 4, 5);

Iterators.removeIf(collection.iterator(), new Predicate<Integer>() {
    @Override
    public boolean apply(Integer i) {
        return i % 2 == 0;
    }
});

System.out.println(collection); // Prints 1, 3, 5

आइए देखें कि अंतर्निहित MutableList सूची और एक्लिप्स संग्रह (पूर्व में जीएस संग्रह ) का उपयोग करके एक MutableList सूची को फ़िल्टर कैसे करें।

List<Integer> jdkList = Arrays.asList(1, 2, 3, 4, 5);
MutableList<Integer> ecList = Lists.mutable.with(1, 2, 3, 4, 5);

यदि आप 3 से कम संख्याओं को फ़िल्टर करना चाहते हैं, तो आप निम्न आउटपुट की अपेक्षा करेंगे।

List<Integer> selected = Lists.mutable.with(1, 2);
List<Integer> rejected = Lists.mutable.with(3, 4, 5);

यहां बताया गया है कि आप अज्ञात आंतरिक वर्ग का उपयोग Predicate रूप में कैसे फ़िल्टर कर सकते हैं।

Predicate<Integer> lessThan3 = new Predicate<Integer>()
{
    public boolean accept(Integer each)
    {
        return each < 3;
    }
};

Assert.assertEquals(selected, Iterate.select(jdkList, lessThan3));

Assert.assertEquals(selected, ecList.select(lessThan3));

यहां जेडीके सूचियों को फ़िल्टर करने के कुछ विकल्प दिए गए हैं और ग्रहण कारखाने का उपयोग कर ग्रहण सूची उत्परिवर्तनीय सूची हैं।

Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));

Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));

यहां एक ऐसा संस्करण है जो भविष्यवाणी के लिए ऑब्जेक्ट आवंटित नहीं करता है, बजाय Predicates2 फैक्ट्री का उपयोग करके selectWith विधि जो Predicate2 लेता है।

Assert.assertEquals(
    selected, ecList.selectWith(Predicates2.<Integer>lessThan(), 3));

कभी-कभी आप नकारात्मक स्थिति पर फ़िल्टर करना चाहते हैं। ग्रहण संग्रह में एक विशेष विधि है जिसे reject कहा जाता है।

Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3));

Assert.assertEquals(rejected, ecList.reject(lessThan3));

यहां बताया गया है कि आप जावा 8 लैम्ब्डा को Predicate रूप में उपयोग करके फ़िल्टर कैसे कर सकते हैं।

Assert.assertEquals(selected, Iterate.select(jdkList, each -> each < 3));
Assert.assertEquals(rejected, Iterate.reject(jdkList, each -> each < 3));

Assert.assertEquals(selected, gscList.select(each -> each < 3));
Assert.assertEquals(rejected, gscList.reject(each -> each < 3));

विधि partition दो संग्रह लौटाएगा, जिसमें चयनित तत्वों द्वारा चयनित तत्वों को खारिज कर दिया गया है।

PartitionIterable<Integer> jdkPartitioned = Iterate.partition(jdkList, lessThan3);
Assert.assertEquals(selected, jdkPartitioned.getSelected());
Assert.assertEquals(rejected, jdkPartitioned.getRejected());

PartitionList<Integer> ecPartitioned = gscList.partition(lessThan3);
Assert.assertEquals(selected, ecPartitioned.getSelected());
Assert.assertEquals(rejected, ecPartitioned.getRejected());

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



जावा 8 ( 2014 ) कोड की एक पंक्ति में धाराओं और लैम्बडा का उपयोग करके इस समस्या को हल करता है:

List<Person> beerDrinkers = persons.stream()
    .filter(p -> p.getAge() > 16).collect(Collectors.toList());

यहां एक tutorial

Collection#removeIf को संशोधित करने के लिए Collection#removeIf का उपयोग करें। (नोटिस: इस मामले में, भविष्यवाणी उन वस्तुओं को हटा देगी जो भविष्यवाणी को पूरा करते हैं):

persons.removeIf(p -> p.getAge() <= 16);

lambdaj लूप या आंतरिक कक्षाओं के बिना संग्रह फ़िल्टरिंग की अनुमति देता है:

List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
    greaterThan(16)));

क्या आप कुछ और पठनीय कल्पना कर सकते हैं?

अस्वीकरण: मैं lambdaj पर एक योगदानकर्ता हूँ


जावा 8 के लिए प्रतीक्षा करें:

List<Person> olderThan30 = 
  //Create a Stream from the personList
  personList.stream().
  //filter the element to select only those with age >= 30
  filter(p -> p.age >= 30).
  //put those filtered elements into a new List.
  collect(Collectors.toList());

जेनिक्स का समर्थन करने वाले एक अद्यतन संग्रह ढांचे के लिए Google संग्रह पर विचार करें।

अद्यतन : Google संग्रह लाइब्रेरी अब बहिष्कृत है। आपको इसके बजाय Guava की नवीनतम रिलीज का उपयोग करना चाहिए। यह अभी भी संग्रह फ्रेमवर्क में सभी समान एक्सटेंशन है जिसमें भविष्यवाणी के आधार पर फ़िल्टरिंग के लिए एक तंत्र शामिल है।


मुझे सूची में मौजूद मूल्यों के आधार पर एक सूची फ़िल्टर करने की आवश्यकता है। उदाहरण के लिए, वर्तमान मान से कम होने वाले सभी मानों को हटा दें। {2 5 3 4 7 5} -> {2 5 7}। या उदाहरण के लिए सभी डुप्लीकेट {3 5 4 2 3 5 6} -> {3 5 4 2 6} को हटाने के लिए।

public class Filter {
    public static <T> void List(List<T> list, Chooser<T> chooser) {
        List<Integer> toBeRemoved = new ArrayList<>();
        leftloop:
        for (int right = 1; right < list.size(); ++right) {
            for (int left = 0; left < right; ++left) {
                if (toBeRemoved.contains(left)) {
                    continue;
                }
                Keep keep = chooser.choose(list.get(left), list.get(right));
                switch (keep) {
                    case LEFT:
                        toBeRemoved.add(right);
                        continue leftloop;
                    case RIGHT:
                        toBeRemoved.add(left);
                        break;
                    case NONE:
                        toBeRemoved.add(left);
                        toBeRemoved.add(right);
                        continue leftloop;
                }
            }
        }

        Collections.sort(toBeRemoved, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });

        for (int i : toBeRemoved) {
            if (i >= 0 && i < list.size()) {
                list.remove(i);
            }
        }
    }

    public static <T> void List(List<T> list, Keeper<T> keeper) {
        Iterator<T> iterator = list.iterator();
        while (iterator.hasNext()) {
            if (!keeper.keep(iterator.next())) {
                iterator.remove();
            }
        }
    }

    public interface Keeper<E> {
        boolean keep(E obj);
    }

    public interface Chooser<E> {
        Keep choose(E left, E right);
    }

    public enum Keep {
        LEFT, RIGHT, BOTH, NONE;
    }
}

यह मधुमक्खी इस तरह इस्तेमाल किया जाएगा।

List<String> names = new ArrayList<>();
names.add("Anders");
names.add("Stefan");
names.add("Anders");
Filter.List(names, new Filter.Chooser<String>() {
    @Override
    public Filter.Keep choose(String left, String right) {
        return left.equals(right) ? Filter.Keep.LEFT : Filter.Keep.BOTH;
    }
});

मेरा जवाब केविन वोंग से उस पर बनाता है, यहां वसंत से CollectionUtils यूटिल्स का उपयोग करके एक लाइनर और जावा 8 लैम्ब्डा अभिव्यक्ति का उपयोग किया जाता है।

CollectionUtils.filter(list, p -> ((Person) p).getAge() > 16);

यह संक्षिप्त और पठनीय है जैसा मैंने देखा है (पहलू-आधारित पुस्तकालयों का उपयोग किए बिना)

वसंत CollectionUtils वसंत संस्करण 4.0.2.RELEASE से उपलब्ध है, और याद रखें कि आपको जेडीके 1.8 और भाषा स्तर 8+ की आवश्यकता है।


मैंने एक विस्तारित इटेरिएबल क्लास लिखा है जो संग्रह सामग्री की प्रतिलिपि किए बिना कार्यात्मक एल्गोरिदम लागू करने का समर्थन करता है।

उपयोग:

List<Integer> myList = new ArrayList<Integer>(){ 1, 2, 3, 4, 5 }

Iterable<Integer> filtered = Iterable.wrap(myList).select(new Predicate1<Integer>()
{
    public Boolean call(Integer n) throws FunctionalException
    {
        return n % 2 == 0;
    }
})

for( int n : filtered )
{
    System.out.println(n);
}

उपरोक्त कोड वास्तव में निष्पादित होगा

for( int n : myList )
{
    if( n % 2 == 0 ) 
    {
        System.out.println(n);
    }
}

यह मानते हुए कि आप जावा 1.5 का उपयोग कर रहे हैं, और आप Google संग्रह नहीं जोड़ सकते हैं, मैं Google के लोगों के समान कुछ करता हूं। यह जॉन की टिप्पणियों पर थोड़ी भिन्नता है।

सबसे पहले इस इंटरफेस को अपने कोडबेस में जोड़ें।

public interface IPredicate<T> { boolean apply(T type); }

इसके कार्यान्वयनकर्ता उत्तर दे सकते हैं जब एक निश्चित भविष्य निश्चित प्रकार के सत्य होता है। उदाहरण के लिए यदि T User थे और AuthorizedUserPredicate<User> User AuthorizedUserPredicate<User> IPredicate<T> लागू IPredicate<T> , तो AuthorizedUserPredicate#apply IPredicate<T> AuthorizedUserPredicate#apply रिटर्न AuthorizedUserPredicate#apply है कि पास में User अधिकृत है या नहीं।

फिर कुछ उपयोगिता वर्ग में, आप कह सकते हैं

public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
    Collection<T> result = new ArrayList<T>();
    for (T element: target) {
        if (predicate.apply(element)) {
            result.add(element);
        }
    }
    return result;
}

तो, यह मानते हुए कि आपके ऊपर उपरोक्त का उपयोग हो सकता है

Predicate<User> isAuthorized = new Predicate<User>() {
    public boolean apply(User user) {
        // binds a boolean method in User to a reference
        return user.isAuthorized();
    }
};
// allUsers is a Collection<User>
Collection<User> authorizedUsers = filter(allUsers, isAuthorized);

यदि रैखिक जांच पर प्रदर्शन चिंता का विषय है, तो हो सकता है कि मैं एक डोमेन ऑब्जेक्ट रखना चाहूंगा जिसमें लक्ष्य संग्रह हो। लक्ष्य ऑब्जेक्ट वाले डोमेन ऑब्जेक्ट में उन विधियों के लिए फ़िल्टरिंग तर्क होगा जो लक्ष्य संग्रह को प्रारंभ और सेट करते हैं।

अद्यतन करें:

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

public class Predicate {
    public static Object predicateParams;

    public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
        Collection<T> result = new ArrayList<T>();
        for (T element : target) {
            if (predicate.apply(element)) {
                result.add(element);
            }
        }
        return result;
    }

    public static <T> T select(Collection<T> target, IPredicate<T> predicate) {
        T result = null;
        for (T element : target) {
            if (!predicate.apply(element))
                continue;
            result = element;
            break;
        }
        return result;
    }

    public static <T> T select(Collection<T> target, IPredicate<T> predicate, T defaultValue) {
        T result = defaultValue;
        for (T element : target) {
            if (!predicate.apply(element))
                continue;
            result = element;
            break;
        }
        return result;
    }
}

निम्नलिखित उदाहरण संग्रहों के बीच अनुपलब्ध वस्तुओं को ढूंढता है:

List<MyTypeA> missingObjects = (List<MyTypeA>) Predicate.filter(myCollectionOfA,
    new IPredicate<MyTypeA>() {
        public boolean apply(MyTypeA objectOfA) {
            Predicate.predicateParams = objectOfA.getName();
            return Predicate.select(myCollectionB, new IPredicate<MyTypeB>() {
                public boolean apply(MyTypeB objectOfB) {
                    return objectOfB.getName().equals(Predicate.predicateParams.toString());
                }
            }) == null;
        }
    });

निम्न उदाहरण, संग्रह में एक उदाहरण की तलाश करता है, और जब इंस्टेंस नहीं मिलता है तो संग्रह का पहला तत्व डिफ़ॉल्ट मान के रूप में देता है:

MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
    return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));

अद्यतन (जावा 8 रिलीज के बाद):

यह कई सालों से हुआ है क्योंकि मैंने (एलन) ने पहले इस जवाब को पोस्ट किया था, और मुझे अभी भी विश्वास नहीं है कि मैं इस जवाब के लिए SO अंक एकत्र कर रहा हूं। किसी भी दर पर, अब जावा 8 ने भाषा को बंद कर दिया है, मेरा जवाब अब काफी अलग और सरल होगा। जावा 8 के साथ, एक अलग स्थिर उपयोगिता वर्ग की कोई आवश्यकता नहीं है। इसलिए यदि आप अपने पहले से मेल खाने वाले पहले तत्व को ढूंढना चाहते हैं।

final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).findFirst();

विकल्प के लिए isPresent() 8 एपीआई में get() , isPresent() , orElse(defaultUser) , या orElseGet(userSupplier) और orElseThrow(exceptionSupplier) , साथ ही map , flatMap और filter जैसे अन्य 'monadic' फ़ंक्शन प्राप्त करने की flatMap

यदि आप भविष्यवाणी से मेल खाने वाले सभी उपयोगकर्ताओं को बस एकत्र करना चाहते हैं, तो वांछित संग्रह में स्ट्रीम को समाप्त करने के लिए Collectors का उपयोग करें।

final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).collect(Collectors.toList());

जावा 8 स्ट्रीम कैसे काम करते here , इस बारे में अधिक उदाहरणों के लिए here देखें।


यहां कुछ वाकई महान महान उत्तर हैं। मैं, मैं जितना संभव हो सके पतले को सरल और पठनीय रखना चाहता हूं:

public abstract class AbstractFilter<T> {

    /**
     * Method that returns whether an item is to be included or not.
     * @param item an item from the given collection.
     * @return true if this item is to be included in the collection, false in case it has to be removed.
     */
    protected abstract boolean excludeItem(T item);

    public void filter(Collection<T> collection) {
        if (CollectionUtils.isNotEmpty(collection)) {
            Iterator<T> iterator = collection.iterator();
            while (iterator.hasNext()) {
                if (excludeItem(iterator.next())) {
                    iterator.remove();
                }
            }
        }
    }
}

सरल प्री-जावा 8 समाधान:

ArrayList<Item> filtered = new ArrayList<Item>(); 
for (Item item : items) if (condition(item)) filtered.add(item);

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



https://code.google.com/p/joquery/

विभिन्न संभावनाओं का समर्थन करता है,

दिया गया संग्रह,

Collection<Dto> testList = new ArrayList<>();

प्रकार का,

class Dto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

फ़िल्टर

जावा 7

Filter<Dto> query = CQ.<Dto>filter(testList)
    .where()
    .property("id").eq().value(1);
Collection<Dto> filtered = query.list();

जावा 8

Filter<Dto> query = CQ.<Dto>filter(testList)
    .where()
    .property(Dto::getId)
    .eq().value(1);
Collection<Dto> filtered = query.list();

इसके अलावा,

Filter<Dto> query = CQ.<Dto>filter()
        .from(testList)
        .where()
        .property(Dto::getId).between().value(1).value(2)
        .and()
        .property(Dto::grtText).in().value(new string[]{"a","b"});

सॉर्टिंग (जावा 7 के लिए भी उपलब्ध है)

Filter<Dto> query = CQ.<Dto>filter(testList)
        .orderBy()
        .property(Dto::getId)
        .property(Dto::getName)
    Collection<Dto> sorted = query.list();

ग्रुपिंग (जावा 7 के लिए भी उपलब्ध है)

GroupQuery<Integer,Dto> query = CQ.<Dto,Dto>query(testList)
        .group()
        .groupBy(Dto::getId)
    Collection<Grouping<Integer,Dto>> grouped = query.list();

जुड़ता है (जावा 7 के लिए भी उपलब्ध है)

दिया हुआ,

class LeftDto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

class RightDto
{
    private int id;
    private int leftId;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getLeftId()
        {
            return leftId;
        }

    public int getText()
    {
        return text;
    }
}

class JoinedDto
{
    private int leftId;
    private int rightId;
    private String text;

    public JoinedDto(int leftId,int rightId,String text)
    {
        this.leftId = leftId;
        this.rightId = rightId;
        this.text = text;
    }

    public int getLeftId()
    {
        return leftId;
    }

    public int getRightId()
        {
            return rightId;
        }

    public int getText()
    {
        return text;
    }
}

Collection<LeftDto> leftList = new ArrayList<>();

Collection<RightDto> rightList = new ArrayList<>();

जैसे शामिल हो सकते हैं,

Collection<JoinedDto> results = CQ.<LeftDto, LeftDto>query().from(leftList)
                .<RightDto, JoinedDto>innerJoin(CQ.<RightDto, RightDto>query().from(rightList))
                .on(LeftFyo::getId, RightDto::getLeftId)
                .transformDirect(selection ->  new JoinedDto(selection.getLeft().getText()
                                                     , selection.getLeft().getId()
                                                     , selection.getRight().getId())
                                 )
                .list();

भाव

Filter<Dto> query = CQ.<Dto>filter()
    .from(testList)
    .where()
    .exec(s -> s.getId() + 1).eq().value(2);

java 8 का उपयोग करना, विशेष रूप से lambda expression , आप इसे नीचे दिए गए उदाहरण की तरह कर सकते हैं:

myProducts.stream().filter(prod -> prod.price>10).collect(Collectors.toList())

जहां मेरे product संग्रह के अंदर प्रत्येक product लिए, अगर prod.price>10 , तो इस उत्पाद को नई फ़िल्टर की गई सूची में जोड़ें।





filter