Каков наилучший способ фильтрации коллекции Java?

void print(const vector<Student>& students)
    {
    for(auto it = students.begin(); it != students.end(); ++it)
        {
            cout << it->name << endl;
        }
    }
631
задан user2864740 12 May 2014 в 05:14
поделиться

11 ответов

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

653
ответ дан 22 November 2019 в 21:49
поделиться

С помощью 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();

Учитывая набор [быстрых, коричневых, лисиц, перепрыгивающих через ленивую собаку], это приводит к [быстрым, коричневым, 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

5
ответ дан akuhn 12 May 2014 в 05:14
поделиться

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

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

или версия 4 использования апачского свободного городского населения org.apache.commons.collections4.iterators. FilterIterator

7
ответ дан Serge Ballesta 12 May 2014 в 05:14
поделиться

Установка:

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();
  }
});
7
ответ дан jon 12 May 2014 в 05:14
поделиться

Рассмотрите Google Collections для обновленной платформы Наборов, которая поддерживает дженерики.

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

62
ответ дан Thiago Arrais 12 May 2014 в 05:14
поделиться

Используйте CollectionUtils.filter (набор, предикат) , от Apache палата общин.

90
ответ дан RAS 12 May 2014 в 05:14
поделиться

"Лучшим" путем является слишком широкий запрос. Действительно ли это является "самым коротким"? "Самый быстрый"? "Читаемый"? Проникнуть на месте или в другой набор?

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

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

65
ответ дан Brad Larson 12 May 2014 в 05:14
поделиться

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

5
ответ дан Jacob Marble 12 May 2014 в 05:14
поделиться

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

Первый добавляют этот интерфейс к Вашей кодовой базе.

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

Его лица, осуществляющие внедрение могут ответить, когда определенный предикат верен для определенного типа. Например, Если T были User и AuthorizedUserPredicate<User> реализации IPredicate<T>, то AuthorizedUserPredicate#apply возвраты, авторизовывается ли переданный в [1 113].

Тогда в некотором служебном классе, Вы могли сказать

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

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

ОБНОВЛЕНИЕ:

В служебном классе (скажем, Предикат), я добавил избранный метод с опцией для значения по умолчанию, когда предикат не возвращает математическое ожидание, и также статическое свойство для параметрических усилителей, которые будут использоваться в новом 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):

Это были несколько лет, с тех пор как я (Alan) сначала отправил этот ответ, и я все еще не могу полагать, что собираю ТАК точки для этого ответа. Во всяком случае, теперь, когда 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();

JDK 8 API для optionals имеет способность к [1 114], isPresent(), orElse(defaultUser), orElseGet(userSupplier) и orElseThrow(exceptionSupplier), а также другие 'одноместные' функции такой как [1 119], 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());

Видят здесь для большего количества примеров о том, как Java 8 потоков работает.

219
ответ дан Alan 12 May 2014 в 05:14
поделиться

Это, в сочетании с отсутствием реальных замкнутых, является моей самой большой бойцом для Java. Честно говоря, большинство упомянутых выше способов довольно легко читать и действительно эффективно; Однако после проведения времени с помощью .NET, Erlang и т. Д. Комплекс списка, интегрированного на уровне языка, делает все намного чище. Без дополнений на уровне языка, Java просто не может быть таким же чистым, как и многие другие языки в этой области.

Если производительность - это огромная забота, коллекции Google - это путь (или написать собственную простую предикатную утилиту). Syntax 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");
3
ответ дан 22 November 2019 в 21:49
поделиться

Я написал расширенный класс 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);
    }
}
2
ответ дан 22 November 2019 в 21:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: