Как лучше всего фильтровать коллекцию Java?

avatar
Kevin Wong
23 сентября 2008 в 16:26
693809
30
717

Я хочу отфильтровать java.util.Collection на основе предиката.

Источник

Ответы (30)

avatar
Mario Fusco
6 сентября 2009 в 13:37
760

Java 8 (2014) решает эту проблему, используя потоки и лямбда-выражения в одной строке кода:

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

Вот учебник.

Используйте Collection#removeIf, чтобы изменить коллекцию на месте. (Примечание: в этом случае предикат удалит объекты, которые удовлетворяют этому предикату):

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

lambdaj позволяет фильтровать коллекции без написания циклов или внутренних классов:

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

Можете представить что-нибудь более читаемое?

Отказ от ответственности: Я участник lambdaj

MikePatel
15 марта 2012 в 11:57
37

Приятно, но статический импорт запутывает происходящее. Для справки выберите / имеющий / включен статический импорт на ch.lambdaj.Lambda, больше чем org.hamcrest.Matchers

Doc Davluz
10 апреля 2012 в 11:54
11

LambdaJ действительно привлекательный, но стоит отметить, что он подразумевает значительные накладные расходы (в среднем 2,6): code.google.com/p/lambdaj/wiki/PerformanceAnalysis.

Moritz
17 мая 2013 в 09:37
7

Видимо не работает на Android: groups.google.com/forum/#!msg/lambdaj/km7uFgvSd3k/grJhgl3ik5sJ

MAbraham1
16 августа 2013 в 14:16
8

Очень нравится этот пример LamdaJ ... похожий на встроенные в .NET лямбда-функции. А где можно пить в 16 лет? Нам следует подумать о добавлении ограничения локализации. :П

Nestor Hernandez Loli
20 июля 2014 в 03:09
1

Бог! Я ненавижу эту утечку с Lambdaj, теперь, когда я использую Java 8, я ищу удаление каждого использования Lambdaj

Evgeni Petrov
8 ноября 2014 в 10:50
1

Я попытался включить Lambdaj, но это было слишком сложно и было нечитаемо для людей, которые не знали Lambdaj. Хотя это только мой опыт.

Ronen Festinger
31 августа 2015 в 22:37
2

Жаль, что он не может использовать перечисления. Я перешел на Apache Commons.

Lii
24 октября 2015 в 13:08
1

Этот ответ был бы еще лучше, если бы он также упомянул простейший метод (начиная с Java 8): Collection.removeIf

Alexis C.
9 декабря 2015 в 00:25
1

Я отредактировал ваш ответ, включив в него Collection#removeIf. Надеюсь, ты не против!

demongolem
25 декабря 2016 в 04:22
1

Интересно, почему верхняя половина не была дана в качестве нового ответа. Если бы это прошло сегодня через конвейер рецензирования, правка, вероятно, была бы отклонена. Сколько голосов за исходный вопрос и сколько за редактирование?

vim
9 февраля 2017 в 14:18
3

removeЕсли пример должен быть persons.removeIf(p -> p.getAge() <= 16);

Oscar Pérez
25 мая 2018 в 10:32
0

Внимание: если вы используете лямбды внутри класса сущности Eclipselink (<2.7), это приведет к странным исключениям.

granadaCoder
4 октября 2018 в 15:02
0

Java (8) наконец догнала dotNet Linq. Ура! # circa2007 en.wikipedia.org/wiki/Language_Integrated_Query

Deian
6 октября 2018 в 05:05
0

что это за перфорация? кто нибудь проверял?

TheOperator
30 апреля 2019 в 11:39
0

Можете представить что-нибудь более читабельное? - вы проверяли Kotlin? ;) persons.filter { it.age >= 16 }

nawfal
24 июня 2020 в 18:10
0

В C # это просто persons.Where(p => p.getAge() > 16).ToList(). Любая идея, почему для Java требуется шаблон, например stream, Collectors.toList и т. Д.?

Iman Marashi
31 октября 2020 в 09:44
0

Для вызова требуется уровень API 24 (текущий мин. 19): `java.util.Collection # removeIf

avatar
Suraj M Nagrale
16 октября 2021 в 05:22
0

потоки фильтров в java используются для фильтрации элементов на основе некоторого условия.

Синтаксис: фильтр потока (предикат предиката)

Пример: из списка чисел, если вы хотите отфильтровать четные или нечетные числа, вы можете использовать метод фильтрации.

Параметры: интерфейс предиката [Представляет функцию, возвращающую логическое значение].

Возвращает: возвращает новый поток с отфильтрованными значениями.

 List < Integer > list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    
 Predicate < Integer > filterEvenNumbers = (number) -> number % 2 == 0;
    
 List < Integer > evenNumbers = list.stream()
          .filter(filterEvenNumbers)
          .collect(toList());
    
        System.out.println(evenNumbers);

O / P: [2, 4, 6, 8, 10]

Дополнительные сведения о потоках фильтров в java см. В статье ниже:

https://getinputs.com/java/the-ultimate-guide-to-java-stream-filter/

avatar
Kevin Lano
3 октября 2021 в 10:26
0

Альтернативной (более легкой) альтернативой потокам коллекций Java является библиотека Ocl.java, которая использует ванильные коллекции и лямбда-выражения: https://github.com/eclipse/agileuml/blob/master/Ocl.java

Например, простой фильтр и сумма слов ArrayList может быть:

ArrayList<Word> sel = Ocl.selectSequence(words, 
                             w -> w.pos.equals("NN")); 
int total = Ocl.sumint(Ocl.collectSequence(sel,
                             w -> w.text.length())); 

Где Word имеет строку pos; Текст строки; атрибуты. Эффективность похожа на параметр потоков, например, 10000 слов обрабатываются примерно за 50 мс в обеих версиях.

Существуют эквивалентные библиотеки OCL для Python, Swift и т. Д. В основном потоки сбора Java заново изобрели операции OCL -> select, -> collect и т. Д., Которые существовали в OCL с 1998 года.

avatar
Muhammad hamed Kamal
27 июля 2021 в 14:56
0

В моем случае я искал список с исключенным определенным полем, равным нулю. Это можно сделать с помощью цикла for и заполнить временный список объектов, у которых нет нулевых адресов. но благодаря Java 8 Streams

List<Person> personsList = persons.stream()
.filter(p -> p.getAdrress() != null).collect(Collectors.toList());

#java #collection #collections # java8 #streams

avatar
pramod_m
18 октября 2018 в 13:40
1

В Java 8 вы можете напрямую использовать этот метод фильтрации, а затем сделать это.

 List<String> lines = Arrays.asList("java", "pramod", "example");

 List<String> result = lines.stream()              
         .filter(line -> !"pramod".equals(line))     
         .collect(Collectors.toList());              

 result.forEach(System.out::println); 
avatar
yanefedor
24 мая 2018 в 13:59
5

Начиная с включена Java 9 Collectors.filtering:

public static <T, A, R>
    Collector<T, ?  R> filtering(Predicate<? super T> predicate,
                                 Collector<? super T, A, R> downstream)

Таким образом, фильтрация должна быть:

collection.stream().collect(Collectors.filtering(predicate, collector))

Пример:

List<Integer> oddNumbers = List.of(1, 19, 15, 10, -10).stream()
            .collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));
avatar
Fredrik Metcalf
15 июня 2017 в 14:28
1

Мне нужно было отфильтровать список в зависимости от значений, уже присутствующих в списке. Например, удалите все следующие значения, которые меньше текущего значения. {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;
    }
});
avatar
ZhekaKozlov
15 декабря 2016 в 09:20
0

С Гуавой:

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
avatar
hd84335
12 декабря 2015 в 21:47
2

Используя java 8, в частности lambda expression, вы можете сделать это просто, как в следующем примере:

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

где для каждого product внутри myProducts коллекции, если prod.price>10, то добавить этот продукт в новый отфильтрованный список.

avatar
vikingsteve
22 апреля 2015 в 11:18
1

Мой ответ основан на ответе Кевина Вонга, здесь в качестве однострочника используется CollectionUtils из spring и выражение Java 8 lambda .

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

Это так же лаконично и читабельно, как и любая альтернатива, которую я видел (без использования аспектно-ориентированных библиотек)

Spring CollectionUtils доступен из Spring версии 4.0.2.RELEASE, и помните, что вам нужен JDK 1.8 и уровень языка 8+.

avatar
Lawrence
7 января 2015 в 11:07
2

Здесь есть действительно отличные ответы. Я, я бы хотел, чтобы все было как можно проще и читабельно:

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();
                }
            }
        }
    }
}
Lawrence
7 января 2015 в 11:09
0

Просто реализуйте правильный excludeItem для каждого фильтра. В конечном итоге у вас будут отдельные фильтры, точно так же, как у вас есть сортировщики в коллекциях ...

avatar
Low Flying Pelican
3 сентября 2014 в 12:59
1

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

Фильтр

Java 7

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

Java 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);
avatar
Brian Bowman
24 июля 2014 в 02:13
11

Я добавлю 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

Более подробную информацию о RxJava filter можно найти здесь.

avatar
Andrew McKnight
3 июля 2014 в 18:54
1

Простое решение до Java8:

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

К сожалению, это решение не является полностью универсальным и выводит список, а не тип данной коллекции. Кроме того, добавление библиотек или написание функций, которые обертывают этот код, кажется мне излишним, если только условие не является сложным, но тогда вы можете написать функцию для этого условия.

avatar
Nestor Hernandez Loli
12 мая 2014 в 05:03
7

Как насчет простой и понятной Java

 List<Customer> list ...;
 List<Customer> newList = new ArrayList<>();
 for (Customer c : list){
    if (c.getName().equals("dd")) newList.add(c);
 }

Просто, понятно и легко (и работает в Android!) Но если вы используете Java 8, вы можете сделать это одной сладкой строкой:

List<Customer> newList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());

Обратите внимание, что toList () импортируется статически

avatar
Josh M
27 октября 2013 в 21:41
11

Начиная с раннего выпуска Java 8, вы могли попробовать что-то вроде:

Collection<T> collection = ...;
Stream<T> stream = collection.stream().filter(...);

Например, если у вас есть список целых чисел, и вы хотите отфильтровать числа, которые больше 10, а затем распечатать эти числа на консоли, вы можете сделать что-то вроде:

List<Integer> numbers = Arrays.asList(12, 74, 5, 8, 16);
numbers.stream().filter(n -> n > 10).forEach(System.out::println);
avatar
gavenkoa
29 августа 2013 в 10:50
31

Ждать 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());
Kevin Wong
30 августа 2013 в 13:38
19

Ух ... это так многословно. Почему они не могли просто сделать: List <Person> result = personList.filter (p -> p.age> 30);

gavenkoa
30 августа 2013 в 13:42
0

@KevinWong Я действительно получил пример из официального руководства, поэтому не знаю другого синтаксиса ...

Kevin Wong
6 сентября 2013 в 18:58
0

Не сомневаюсь, что это официальный синтаксис. Я просто говорю, что это отстой.

gavenkoa
7 сентября 2013 в 13:55
8

Чтобы использовать фильтр непосредственно в Коллекции , вам необходимо использовать removeIf позвонить: download.java.net/jdk8/docs/api/java/util/…

Rogue
12 мая 2014 в 05:09
7

@KevinWong "многословный" в значительной степени описывает весь язык, я думаю. По крайней мере, они последовательны?

Nestor Hernandez Loli
12 мая 2014 в 13:45
5

Почему бы не использовать Collectors.toList () в последней части?

Captain Man
16 июня 2015 в 20:58
3

Здесь - ссылка gavenkoa при условии, что не 404. personList.removeIf(p -> p.age < 30); Менее подробный. Кроме того, я слышал разговоры о начале реализации API, которые принимают и возвращают Stream, а не Collection, потому что Stream очень полезны и быстры, но переход к ним / от них происходит медленно.

avatar
Donald Raab
24 сентября 2012 в 22:28
7

Давайте посмотрим, как отфильтровать встроенный список JDK и MutableList с помощью Коллекции Eclipse.

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

Вот как можно отфильтровать лямбда-выражение Java 8 в качестве Predicate.

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

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

Вот как можно отфильтровать, используя анонимный внутренний класс в качестве 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 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 Collections есть специальный метод для этого под названием reject.

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

Метод 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 Collections.

Vivek Rao
16 октября 2017 в 21:03
1

Как бы вы сделали removeIf в списке или наборе для примитивов?

Donald Raab
1 февраля 2020 в 07:55
0

API для removeIf был добавлен к примитивным коллекциям в EC 9.1. eclipse.org/collections/javadoc/9.1.0/org/eclipse/collections/…

avatar
npgall
29 июля 2012 в 22:12
2

Используйте механизм сбора запросов (CQEngine). На сегодняшний день это самый быстрый способ сделать это.

См. Также: Как вы запрашиваете коллекции объектов в Java (критерии / SQL-подобные)?

avatar
Kamran Ali Khan
5 апреля 2012 в 10:46
2

JFilter http://code.google.com/p/jfilter/ лучше всего подходит для ваших требований.

JFilter - это простая и высокопроизводительная библиотека с открытым исходным кодом для запроса коллекции компонентов Java.

Ключевые особенности

  • Поддержка свойств коллекции (java.util.Collection, java.util.Map и Array).
  • Поддержка сбора внутри коллекции любой глубины.
  • Поддержка внутренних запросов.
  • Поддержка параметризованных запросов.
  • Может фильтровать 1 миллион записей за несколько 100 мс.
  • Фильтр (запрос) задается в простом формате json, он похож на запросы Mangodb. Ниже приведены некоторые примеры.
  • {"id": {"$ le": "10"}
    • , где свойство id объекта меньше 10.
  • {"id": {"$ in": ["0", "100"]}}
    • , где свойство id объекта равно 0 или 100.
  • {"lineItems": {"lineAmount": "1"}}
    • где свойство коллекции lineItems параметризованного типа имеет значение lineAmount, равное 1.
  • {"$ and": [{"id": "0"}, {"billingAddress": {"city": "DEL"}}]}
    • , где свойство id равно 0, а свойство billingAddress.city - DEL.
  • {"lineItems": {"Tax": {"key": {"code": "GST"}, "value": {"$ gt": "1.01"}}}}
    • где свойство коллекции lineItems параметризованного типа, которое имеет свойство типа карты налогов параметризованного типа, имеет код, равный значению GST больше 1,01.
  • {'$ or': [{'code': '10'}, {'skus': {'$ and': [{'price': {'$ in': ['20', '40 ']}}, {' code ':' RedApple '}]}}]}
    • Выберите все продукты, у которых код продукта - 10, цена - 20 и 40, а код - RedApple.
assylias
5 апреля 2012 в 11:01
1

Вы должны отказаться от того, что являетесь автором (как я думаю).

Kamran Ali Khan
6 апреля 2012 в 05:50
0

Да, я являюсь автором этой библиотеки.

avatar
Vincent Robert
5 апреля 2010 в 12:43
2

Я написал расширенный класс 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);
    }
}
avatar
jdc0589
21 января 2010 в 04:35
3

Это, в сочетании с отсутствием реальных закрытий, является моей самой большой проблемой для Java. Честно говоря, большинство упомянутых выше методов довольно легко читаются и ДЕЙСТВИТЕЛЬНО эффективны; однако, проведя время с .Net, Erlang и т. д., понимание списков, интегрированное на уровне языка, делает все намного чище. Без дополнений на уровне языка Java не может быть такой же чистой, как многие другие языки в этой области.

Если производительность вызывает огромную озабоченность, можно использовать коллекции 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");
MatrixFrog
20 июня 2011 в 22:39
0

Ссылка на сайт? Даже если ваша библиотека неэффективна или непригодна для использования по другим причинам, может быть интересно посмотреть, доступен ли исходный код.

jdc0589
23 июня 2011 в 03:02
0

Сделал репо общедоступным (net-machine.com/indefero/p/jdclib/source/tree/master). Вас интересует пакет выражений. В тестовом пакете есть тестер с примером использования. Я никогда особо не работал над интерфейсом строкового запроса, о котором говорилось выше (не хотелось писать настоящий синтаксический анализатор), поэтому явный интерфейс запроса в тестере - лучший вариант.

avatar
akuhn
3 декабря 2008 в 13:45
5

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

Учитывая набор [The, quick, brown, fox, jumps, over, the, lazy, dog], получается [quick, brown, jumps, over, lazy], то есть все строки длиннее трех символов.

Все стили итерации, поддерживаемые ForEach DSL:

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

Для получения более подробной информации обратитесь к https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach

oxbow_lakes
25 февраля 2009 в 21:47
0

Это очень умно! Однако предстоит проделать большую работу по реализации красивого синтаксиса в стиле Ruby! Отрицательным является то, что ваш фильтр не является первоклассной функцией и, следовательно, не может быть использован повторно. Закатывающиеся застежки ...

akuhn
28 февраля 2009 в 16:00
0

Хорошая точка зрения. Один из способов повторно использовать тело цикла - преобразовать цикл в метод, который принимает запрос выбора в качестве параметра. Однако это, конечно, далеко не так удобно и мощно, как настоящие закрытия.

avatar
Kevin Wong
23 сентября 2008 в 18:21
5

Метод Collections2.filter (Collection, Predicate) в библиотеке Google Guava делает именно то, что вы ищете.

avatar
Alan
23 сентября 2008 в 16:41
228

Предполагая, что вы используете Java 1.5 и не можете добавить Коллекции Google, я бы сделал что-то очень похожее на то, что сделали ребята из Google. Это небольшое изменение комментариев Джона.

Сначала добавьте этот интерфейс в свою кодовую базу.

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

Его разработчики могут ответить, когда определенный предикат является истинным для определенного типа. Например. Если T было User, а AuthorizedUserPredicate<User> реализует IPredicate<T>, то 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);

Если производительность при линейной проверке вызывает беспокойство, тогда мне может потребоваться объект домена с целевой коллекцией. Объект домена, имеющий целевую коллекцию, будет иметь логику фильтрации для методов, которые инициализируют, добавляют и задают целевую коллекцию.

ОБНОВЛЕНИЕ:

В служебном классе (скажем, 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));

ОБНОВЛЕНИЕ (после выпуска Java 8):

Прошло несколько лет с тех пор, как я (Алан) впервые опубликовал этот ответ, и я до сих пор не могу поверить, что набираю ТАК баллов за этот ответ. Во всяком случае, теперь, когда Java 8 ввела в язык замыкания, мой ответ был бы значительно другим и более простым. В Java 8 нет необходимости в отдельном статическом служебном классе. Итак, если вы хотите найти 1-й элемент, соответствующий вашему предикату.

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

API JDK 8 для дополнительных опций имеет возможность get(), isPresent(), orElse(defaultUser), orElseGet(userSupplier) и orElseThrow(exceptionSupplier), а также другие «монадические» функции, такие как <

15882>, <

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

См. здесь для получения дополнительных примеров того, как работают потоки Java 8.

Kevin Wong
25 сентября 2008 в 18:18
27

Да, но я ненавижу изобретать велосипед снова и снова. Я бы предпочел найти какую-нибудь служебную библиотеку, которая работает, когда захочу.

Josh
11 октября 2008 в 19:54
2

Это не лучший способ, если вам не нужна новая коллекция. Используйте метафору фильтрующего итератора, которая может входить в новую коллекцию, или это может быть все, что вам нужно.

Alan
3 июня 2014 в 20:33
0

@Nestor: в понимании Scala фильтрация была бы намного проще: val authorized = for (user <- users if user.isAuthorized) yield user

Rohan
14 марта 2016 в 12:45
0

Это изменит исходную коллекцию или создаст новую? Я попробовал использовать этот метод и зарегистрировал обе свои коллекции (исходную и возвращенную методом), они одинаковы. @Алан

Alan
11 июня 2016 в 16:04
1

@Rohan, это не предназначено для изменения исходной коллекции. Обратите внимание, что приведенная выше коллекция результатов создается заново, и метод фильтра добавляет в нее только в том случае, если применяется предикат.

avatar
Vladimir Dyuzhev
23 сентября 2008 в 16:41
67

"Лучший" способ - это слишком широкий запрос. Он самый «короткий»? «Самый быстрый»? "Удобочитаемый"? Фильтровать на месте или в другую коллекцию?

Самый простой (но не самый читаемый) способ - повторить его и использовать метод 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 () выполняет итерацию по коллекции и вызывает Predicate.keepIt (), чтобы узнать, следует ли сохранить экземпляр в коллекции.

Я действительно не вижу оправдания для использования сторонней библиотеки только для этой задачи.

ZeroOne
21 декабря 2012 в 11:40
6

Я голосую за это: он просто работает, без внешних библиотек. Я никогда не понимал, что создание экземпляра Iterator может быть действительно полезным по сравнению с использованием синтаксиса for-each или что вы можете удалять элементы из списка без ConcurrentModificationException или чего-то в этом роде. :)

Populus
12 декабря 2014 в 15:38
1

Я считаю, что это лучший способ использовать стандартную библиотеку Java без копирования. В версии 1.8 будет функция stream(), но не все смогут играть с новейшими игрушками: P

Rohan
15 марта 2016 в 02:46
0

Это тоже меняет исходную коллекцию? @ZeroOne

ZeroOne
15 марта 2016 в 03:39
0

Да, конечно, @Rohan. Попробуйте, если не верите. ;)

Rohan
15 марта 2016 в 04:13
0

Ха-ха, я сделал! Но я хочу сохранить свою оригинальную коллекцию. Можете ли вы предложить способ сделать это без добавления внешней библиотеки? @ZeroOne

avatar
jon
23 сентября 2008 в 16:41
7

Настройка:

public interface Predicate<T> {
  public boolean filter(T t);
}

void filterCollection(Collection<T> col, Predicate<T> predicate) {
  for (Iterator i = col.iterator(); i.hasNext();) {
    T obj = i.next();
    if (predicate.filter(obj)) {
      i.remove();
    }
  }
}

Использование:

List<MyObject> myList = ...;
filterCollection(myList, new Predicate<MyObject>() {
  public boolean filter(MyObject obj) {
    return obj.shouldFilter();
  }
});
marcospereira
24 сентября 2008 в 03:45
2

Хорошо, но я предпочитаю реализацию Alan, потому что вы получаете копию коллекции вместо ее изменения. Более того, код Алана является потокобезопасным, а ваш - нет.

avatar
ykaganovich
23 сентября 2008 в 16:41
7

Вы уверены, что хотите отфильтровать саму коллекцию, а не итератор?

см. org.apache.commons.collections.iterators.FilterIterator

или используя версию 4 apache commons org.apache.commons.collections4.iterators.FilterIterator

avatar
Heath Borders
23 сентября 2008 в 16:29
62

Рассмотрим Коллекции Google для обновленной платформы Коллекций, которая поддерживает универсальные шаблоны.

ОБНОВЛЕНИЕ : библиотека коллекций Google устарела. Вместо этого вы должны использовать последний выпуск Guava. Он по-прежнему имеет все те же расширения структуры коллекций, включая механизм фильтрации на основе предиката.

Kevin Wong
23 сентября 2008 в 18:25
0

да, я знал про гугловские коллекции lib. В версии, которую я использовал, не было Collections2. Я добавил новый ответ на этот вопрос, в котором перечислены конкретные методы.

Kevin Bourrillion
8 ноября 2009 в 16:43
7

Kevin, Iterables.filter () и Iterators.filter () были там с самого начала, и обычно это все, что вам нужно.

avatar
Kevin Wong
23 сентября 2008 в 16:28
93

Используйте CollectionUtils.filter (Collection, Predicate) из Apache Commons.

Kevin Wong
23 сентября 2008 в 16:30
3

это нормально, но не является универсальным и изменяет коллекцию на месте (нехорошо)

skaffman
6 сентября 2009 в 15:08
2

В CollectionUtils есть и другие методы фильтрации, которые не изменяют исходную коллекцию.

Eero
29 сентября 2010 в 12:27
43

В частности, метод, который , а не изменяет коллекцию на месте, - это org.apache.commons.collections.CollectionUtils # select (Collection, Predicate)

Justin Emery
16 июня 2014 в 19:26
5

В Commons Collections v4 теперь используются Generics.

user2417480
15 октября 2015 в 12:18
1

Этот метод следует использовать с осторожностью, поскольку он полагается (по крайней мере, в реализации commons-collections-3.2.1) на метод iterator.remove (), который является необязательным для коллекций, поэтому вместо фильтрации, скажем, массива вы можете получить исключение UnsupportedOperationException.