2015-01-20 2 views
1

Использование потока API, как можно фильтровать после сбора с помощью операции groupingBy ->counting, содержат записи на основе фильтра количества вхождений?Как я могу отфильтровать записи на основе количества встречаемости?

Учитывая следующее:

Map<Integer, Long> counts = Stream.of(1, 2, 2, 3, 4, 5, 5) 
     .collect(groupingBy(n -> n, counting())); 

Как отфильтровать это содержать только ключи 2 и 5?

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

Map<Integer, Long> counts = Stream.of(1, 2, 2, 3, 4, 5, 5) 
     .collect(groupingBy(n -> n, counting())) 
     .entrySet().stream() 
     .filter(n -> n.getValue() > 1) 
     .collect(toMap(Entry::getKey, Entry::getValue)); 

ответ

3

Невозможно создать карту или подобную структуру данных для операции, которая зависит от уже наблюдаемых значений. То же самое происходит с, например, distinct, который выглядит как шаг в цепочке операций, но не может работать без создания карты (или структуры, подобной карте) внутри.

Вы можете сделать все, что выглядит как одну операцию потока с использованием

Map<Integer, Long> counts = Stream.of(1, 2, 2, 3, 4, 5, 5) 
    .collect(collectingAndThen(groupingBy(n -> n, counting()), 
     map -> map.entrySet().stream() 
     .filter(n -> n.getValue() > 1) 
     .collect(toMap(Entry::getKey, Entry::getValue)) 
    )); 

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

Обратите внимание, что иногда операция не поток может выглядеть более кратким:

Map<Integer, Long> counts = Stream.of(1, 2, 2, 3, 4, 5, 5) 
    .collect(groupingBy(n -> n, HashMap::new, counting())); 
counts.values().removeIf(count -> count < 2); 

Если вы заинтересованы в обработке самой детали в пределах (параллельно дееспособными) Stream, не заботясь о фактическом количестве вхождений, вот простое решение:

ConcurrentHashMap<Integer,Integer> counts=new ConcurrentHashMap<>(); 
Stream.of(1, 2, 2, 3, 4, 5, 5) 
     .filter(i -> counts.merge(i, 1, Integer::sum)==2) 
     .forEach(System.out::println); 

это позволяет применять в последующие операции до работы терминала, как только SECON d предмет не встречался без необходимости обрабатывать все элементы или ждать окончания потока и согласовываться с выполнением и/или закорачиванием parallel, такими как limit или findAny и т. д.

+0

точка, отличающаяся работает над параллелью, я уверен, что может быть предоставлен spliterator, который будет содержать число совпадений, принимающих первое, которое удовлетворяет функциональности (для большего, чем пример). Разделителю нужны аргументы для контроля, если все для того же нужно посетить, прежде чем принимать счет. Если поток сортируется, каждый прием будет происходить раньше, если объект будет проходить дальше в потоковом конвейере. –

+1

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

+0

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

Смежные вопросы