Вопрос по java – Библиотечный метод для разделения коллекции по предикату

27

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

Я хотел бы изобразить подпись метода будет что-то вроде этого:

public static <E> Pair<Collection<E>, Collection<E>> partition(Collection<E> source, Predicate<? super E> predicate)

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

Другой подход, дружественный к GC и инкапсулированный, заключается в использовании потоков фильтрации Java 8 вокруг оригинальной коллекции:stackoverflow.com/questions/19940319/… Vadzim
Обратите внимание, что в случае ограниченного набора известных заранее ключей разделения, GC может быть намного эффективнее, просто повторить коллекцию еще раз для каждого ключа разделения, пропуская все элементы с разными ключами на каждой итерации. Vadzim

Ваш Ответ

5   ответов
3

Если вы используетеКоллекции Затмения (ранее GS Collections), вы можете использоватьpartition метод на всехRichIterables.

MutableList<Integer> integers = FastList.newListWith(-3, -2, -1, 0, 1, 2, 3);
PartitionMutableList<Integer> result = integers.partition(IntegerPredicates.isEven());
Assert.assertEquals(FastList.newListWith(-2, 0, 2), result.getSelected());
Assert.assertEquals(FastList.newListWith(-3, -1, 1, 3), result.getRejected());

Причина использования пользовательского типа,PartitionMutableList, вместоPair разрешить ковариантные типы возврата для getSelected () и getRejected (). Например, разбиениеMutableCollection дает две коллекции вместо списков.

MutableCollection<Integer> integers = ...;
PartitionMutableCollection<Integer> result = integers.partition(IntegerPredicates.isEven());
MutableCollection<Integer> selected = result.getSelected();

Если ваша коллекция неRichIterableвы все еще можете использовать статическую утилиту в коллекциях Eclipse.

PartitionIterable<Integer> partitionIterable = Iterate.partition(integers, IntegerPredicates.isEven());
PartitionMutableList<Integer> partitionList = ListIterate.partition(integers, IntegerPredicates.isEven());

Note: Я коммиттер Eclipse Collections.

11

С новыми функциями Java 8 (поток а такжелямбда-экспрессии), вы могли бы написать:

List<String> words = Arrays.asList("foo", "bar", "hello", "world");

Map<Boolean, List<String>> partitionedMap =
        words.stream().collect(
                Collectors.partitioningBy(word -> word.length() > 3));

System.out.println(partitionedMap);
24

Использовать гуавуMultimaps.index.

Вот пример, который разбивает список слов на две части: те, которые имеют длину & gt; 3 и те, которые не имеют.

List<String> words = Arrays.asList("foo", "bar", "hello", "world");

ImmutableListMultimap<Boolean, String> partitionedMap = Multimaps.index(words, new Function<String, Boolean>(){
    @Override
    public Boolean apply(String input) {
        return input.length() > 3;
    }
});
System.out.println(partitionedMap);

печатает:

false=[foo, bar], true=[hello, world]
@PeterandtheWolf я добавил ответ, основываясь на вашем предложении
Обновление для Java 8: аналогичный метод также предоставляется в потоковом пакетеjava.util.stream.Collectors#groupingBy(java.util.function.Function<....>)  Это идет какwords.stream().collect(Collectors.groupingBy(func))
Если у вас уже есть предикат, вы можете превратить его в функцию с помощью Functions.forPredicate.
Спасибо, я бы не подумала посмотреть там. Edward Dale
0

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

LocalDate start = LocalDate.now().with(TemporalAdjusters.firstDayOfYear());
LocalDate endExclusive = LocalDate.now().plusYears(1);
List<LocalDate> daysCollection = Stream.iterate(start, date -> date.plusDays(1))
        .limit(ChronoUnit.DAYS.between(start, endExclusive))
        .collect(Collectors.toList());
List<DayOfWeek> keys = Arrays.asList(DayOfWeek.values());

for (DayOfWeek key : keys) {
    int count = 0;
    for (LocalDate day : daysCollection) {
        if (key == day.getDayOfWeek()) {
            ++count;
        }
    }
    System.out.println(String.format("%s: %d days in this year", key, count));
}

Другой подход, совместимый с GC и инкапсулированный, заключается в использовании потоков-оболочек фильтрации Java 8 вокруг исходной коллекции:

List<AbstractMap.SimpleEntry<DayOfWeek, Stream<LocalDate>>> partitions = keys.stream().map(
        key -> new Abs,tractMap.SimpleEntry<>(
                key, daysCollection.stream().filter(
                    day -> key == day.getDayOfWeek())))
        .collect(Collectors.toList());
// partitions could be passed somewhere before being used
partitions.forEach(pair -> System.out.println(
        String.format("%s: %d days in this year", pair.getKey(), pair.getValue().count())));

Оба фрагмента печатают это:

MONDAY: 57 days in this year
TUESDAY: 57 days in this year
WEDNESDAY: 57 days in this year
THURSDAY: 57 days in this year
FRIDAY: 56 days in this year
SATURDAY: 56 days in this year
SUNDAY: 56 days in this year
0

Коллекции Apache Commons IterableUtils предоставляет методы для разделенияIterable объекты на основе одного или нескольких предикатов. (Ищитеpartition(...) методы.)

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