ما هي أفضل طريقة لتصفية مجموعة Java؟




collections filter (17)

JFilter http://code.google.com/p/jfilter/ هو الأنسب لاحتياجاتك.

JFilter هي مكتبة مفتوحة المصدر ذات أداء عالٍ وعالي الجودة للاستعلام عن تجميع الفاصوليا Java.

دلائل الميزات

  • دعم مجموعة (java.util.Collection، java.util.Map و Array) خصائص.
  • دعم جمع داخل جمع من أي عمق.
  • دعم الاستعلامات الداخلية.
  • دعم الاستعلامات المعلمات.
  • يمكن تصفية 1 مليون سجل في بضع 100 مللي ثانية.
  • يتم إعطاء فلتر (استعلام) في تنسيق json بسيط ، هو مثل استعلامات Mangodb. فيما يلي بعض الأمثلة.
  • {"id": {"$ le": "10"}
    • حيث تكون خاصية معرف الكائن أقل من تساوي 10.
  • {"id": {"$ in": ["0"، "100"]}}
    • حيث تكون خاصية معرف الكائن هي 0 أو 100.
  • { "lineItems": { "lineAmount": "1"}}
    • حيث خاصية lineItems collection لنوع ذو معلمات تساوي lineAmount تساوي 1.
  • {"$ و": [{"id": "0"}، {"billingAddress": {"city": "DEL"}}]}
    • حيث أن خاصية المعرف هي 0 وأن خاصية billingAddress.city هي DEL.
  • {"lineItems": {"taxes": {"key": {"code": "GST"} ، "value": {"$ gt": "1.01"}}}}
    • حيث أن خاصية جمع lineItems لنوع ذي معلمات والتي تحتوي على خاصية نوع الخريطة الضريبية لنوع parameteriszed لها كود مساوي لقيمة GST أكبر من 1.01.
  • {'$ أو': [{'code': '10'}، {'skus': {'$ and': [{'price': {'$ in': ['20'، '40']} } ، {'code': 'RedApple'}]}}}}
    • حدد جميع المنتجات التي يكون رمز المنتج فيها 10 أو سعر sku في 20 و 40 ورمز sku هو "RedApple".

أريد تصفية java.util.Collection استنادًا إلى دالة تقييم.


أفضل طريقة هي طلب واسع للغاية. هل هو "أقصر"؟ "أسرع"؟ "المقروءه"؟ تصفية في مكان أو في مجموعة أخرى؟

أبسط طريقة (ولكن ليس أكثر قابلية للقراءة) هي تكرار ذلك واستخدام أسلوب Iterator.remove ():

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

الآن ، لجعله أكثر قابلية للقراءة ، يمكنك لفه في طريقة فائدة. ثم اختر واجهة IPredicate ، وقم بإنشاء تطبيق مجهول لتلك الواجهة وفعل ما يلي:

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

حيث filterInPlace () iterate المجموعة و المكالمات Predicate.keepIt () لمعرفة ما إذا كان سيتم الاحتفاظ المثيل في المجموعة.

لا أرى حقاً مبررًا لإحضار مكتبة خارجية فقط لهذه المهمة.


استخدم محرك استعلام مجموعة (CQEngine) . إنها أسرع طريقة للقيام بذلك.

راجع أيضًا: كيف يمكنك الاستعلام عن مجموعات الكائنات في Java (المعايير / SQL-like)؟


انتظر Java 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());

بافتراض أنك تستخدم Java 1.5 ، وأنه لا يمكنك إضافة مجموعات Google ، سأفعل شيئًا مشابهًا لما فعله Google. هذا هو اختلاف طفيف على تعليقات جون.

أضف أولاً هذه الواجهة إلى مصدر التعليمات البرمجية الخاص بك.

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

يمكن لمنفِّذيه الإجابة عندما يكون أحد المسندات صحيحًا لنوع معين. على سبيل المثال ، إذا كانت T IPredicate<T> و AuthorizedUserPredicate<User> بتطبيق IPredicate<T> ، IPredicate<T> تطبيق AuthorizedUserPredicate#apply إرجاع ما إذا كان IPredicate<T> في 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);

إذا كان الأداء في الفحص الخطي مثيرًا للقلق ، فقد أرغب في الحصول على كائن مجال يحتوي على المجموعة المستهدفة. سيكون لدى كائن المجال الذي يحتوي على المجموعة الهدف منطقية التصفية للأساليب التي تضيف ، إضافة وتعيين مجموعة الهدف.

تحديث:

في فئة الأداة المساعدة (دعنا نفترض Predicate) ، قمت بإضافة طريقة تحديد مع خيار للقيمة الافتراضية عندما لا تقوم دالة التقييم بإرجاع القيمة المتوقعة ، وكذلك خاصية ثابتة من أجل استخدام المعلمات داخل IPredicate الجديد.

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));

UPDATE (بعد إصدار Java 8):

لقد مرت عدة سنوات منذ أن نشرت (آلان) هذه الإجابة لأول مرة ، وما زلت لا أصدق أنني أحصل على نقاط SO لهذه الإجابة. على أي حال ، الآن بعد أن أدخلت Java 8 الإغلاق إلى اللغة ، ستكون إجابتي مختلفة بشكل كبير ، وأبسط. مع Java 8 ، ليست هناك حاجة لفئة فائدة ثابتة متميزة. لذلك إذا كنت تريد العثور على العنصر الأول الذي يتطابق مع المسند الخاص بك.

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

إن واجهة برمجة التطبيقات JDK 8 الاختيارية لديها القدرة على get() أو isPresent() أو orElse(defaultUser) و / orElseGet(userSupplier) و orElseThrow(exceptionSupplier) ، بالإضافة إلى وظائف "monadic" أخرى مثل map و flatMap filter .

إذا كنت ترغب ببساطة في جمع كل المستخدمين الذين يتطابقون مع المسند ، فاستخدم 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());

انظر here لمزيد من الأمثلة حول كيفية عمل تيارات جافا 8.


بعض الإجابات الرائعة الرائعة هنا. أنا ، أود أن أبقي الأمور بسيطة وسهلة القراءة قدر الإمكان:

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();
                }
            }
        }
    }
}

تعمل Java 8 ( 2014 ) على حل هذه المشكلة باستخدام الدفقات ولامبدات في سطر واحد من التعليمات البرمجية:

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

وهنا tutorial .

استخدم Collection#removeIf لتعديل المجموعة في المكان. (ملاحظة: في هذه الحالة ، سيقوم المسند بإزالة الكائنات التي تستوفي المسند):

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

يسمح lambdaj بتصفية المجموعات دون كتابة الحلقات أو الطبقات الداخلية:

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

يمكنك تخيل شيء أكثر قابلية للقراءة؟

إخلاء المسؤولية: أنا مساهم في lambdaj



دعونا ننظر في كيفية تصفية قائمة JDK MutableList باستخدام مجموعات Eclipse ( مجموعات GS السابقة ).

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));

فيما يلي بعض البدائل لترشيح قوائم JDK وقوائم Eclipse Collections MutableLists باستخدام مصنع Predicates .

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));

في بعض الأحيان تريد تصفية على شرط سلبي. هناك طريقة خاصة في مجموعات Eclipse التي تسمى reject .

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

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

إليك كيف يمكنك التصفية باستخدام Java 8 lambda مثل 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 الطريقة مجموعتين تحتويان على العناصر المحددة بواسطة Predicate ورفضه.

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());

ملاحظة: أنا ملتزم بمجموعات Eclipse.


سوف أرمي RxJava في الحلقة ، والتي تتوفر أيضًا على Android . قد لا يكون RxJava دائمًا الخيار الأفضل ، ولكنه يمنحك المزيد من المرونة إذا كنت ترغب في إضافة المزيد من التحويلات في مجموعتك أو معالجة الأخطاء أثناء التصفية.

Observable.from(Arrays.asList(1, 2, 3, 4, 5))
    .filter(new Func1<Integer, Boolean>() {
        public Boolean call(Integer i) {
            return i % 2 != 0;
        }
    })
    .subscribe(new Action1<Integer>() {
        public void call(Integer i) {
            System.out.println(i);
        }
    });

انتاج:

1
3
5

يمكن العثور على مزيد من التفاصيل حول filter RxJava here .


كنت بحاجة إلى تصفية قائمة اعتمادًا على القيم الموجودة بالفعل في القائمة. على سبيل المثال ، إزالة كل القيم التالية أقل من القيمة الحالية. {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;
    }
});

لقد كتبت فصلًا ممتدًا من Iterable يدعم تطبيق الخوارزميات الوظيفية دون نسخ محتوى المجموعة.

الاستعمال:

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);
    }
}

مع ForEach DSL قد تكتب

import static ch.akuhn.util.query.Query.select;
import static ch.akuhn.util.query.Query.$result;
import ch.akuhn.util.query.Select;

Collection<String> collection = ...

for (Select<String> each : select(collection)) {
    each.yield = each.value.length() > 3;
}

Collection<String> result = $result();

بالنظر إلى مجموعة من [، سريع ، بني ، ثعلب ، يقفز ، أكثر ، كسل ، كلب] هذا ينتج عنه [سريع ، بني ، يقفز ، أكثر ، كسول] ، أي جميع السلاسل الطويلة التي تزيد عن ثلاثة أحرف.

كل أنماط التكرار التي يدعمها ForEach DSL هي

  • AllSatisfy
  • AnySatisfy
  • Collect
  • Counnt
  • CutPieces
  • Detect
  • GroupedBy
  • IndexOf
  • InjectInto
  • Reject
  • Select

لمزيد من التفاصيل ، يرجى الرجوع إلى https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach


مع الجوافة:

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

هذا ، جنبا إلى جنب مع عدم وجود إغلاق الحقيقي ، هو أكبر وجع بلدي لجاوا. بصراحة ، معظم الطرق المذكورة أعلاه هي سهلة القراءة جدا وفعالة حقا. ومع ذلك ، بعد قضاء بعض الوقت مع .Net ، Erlang ، الخ ... فهم القائمة المتكاملة على مستوى اللغة يجعل كل شيء أكثر نظافة. بدون إضافات على مستوى اللغة ، لا يمكن أن تكون جافا نظيفة مثل العديد من اللغات الأخرى في هذا المجال.

إذا كان الأداء يمثل مصدر قلق كبير ، فإن مجموعات Google هي طريقة الانتقال (أو كتابة الأداة المساعدة البسيطة الخاصة بك). بنية Lambdaj أكثر قابلية للقراءة لدى بعض الأشخاص ، ولكنها ليست بنفس الكفاءة.

ثم هناك مكتبة كتبت. سوف أتجاهل أية أسئلة فيما يتعلق بكفاءتها (نعم ، إنها سيئة) ...... نعم ، أنا أعرف تأملها بشكل واضح ، ولا لا أستخدمها فعلاً ، لكنها تعمل:

LinkedList<Person> list = ......
LinkedList<Person> filtered = 
           Query.from(list).where(Condition.ensure("age", Op.GTE, 21));

أو

LinkedList<Person> list = ....
LinkedList<Person> filtered = Query.from(list).where("x => x.age >= 21");


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"});

الفرز (متاح أيضًا لـ Java 7)

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

التجميع (متاح أيضًا لـ Java 7)

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

الانضمام (متوفر أيضًا لـ Java 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);




filter