Использование потока, фильтра и среднего по списку и jdk8

1

У меня есть этот список данных, которые выглядят так:

{id, datastring}

{1,"a:1|b:2|d:3"}
{2,"a:2|c:2|c:4"}
{3,"a:2|bb:2|a:3"}
{4,"a:3|e:2|ff:3"}

Мне нужно сделать такие операции, как среднее или найти все id, для которых элемент в строке меньше определенного значения.

Вот несколько примеров;

Сред

{a,2}{b,2}{bb,2}{c,3}{d,3}{e,2}{ff,3}

Найти все id, где c <4

{2}

Найти все id, где a <3

{1,2,3}

Будет ли это хорошим использованием stream() и filter()?

  • 1
    Да. Это было бы. (если я правильно понял)
Теги:
list
java-8
filter
java-stream

2 ответа

0
Лучший ответ

Я знаю, что у вас есть ответ, но вот мои версии тоже:

 Map<String, Double> result = list.stream()
            .map(Data::getElements)
            .flatMap((Multimap<String, Integer> map) -> {
                return map.entries().stream();
            })
            .collect(Collectors.groupingBy(Map.Entry::getKey,
                    Collectors.averagingInt((Entry<String, Integer> token) -> {
                        return token.getValue();
                    })));
    System.out.println(result);

    List<Integer> result2 = list.stream()
            .filter((Data data) -> {
                return data.getElements().get("c").stream().anyMatch(i -> i < 4);
            })
            .map(Data::getId)
            .collect(Collectors.toList());
    System.out.println(result2);
1

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

class Data {
    private int id;
    private Map<String, List<Integer>> map;
    ....
}

Тем не менее, давайте взглянем на то, как вы можете это реализовать. Во-первых, найти всю реализацию:

public static Set<Integer> ids(List<Data> list, String value, Predicate<Integer> boundPredicate) {
    return list.stream()
               .filter(d -> d.getMap().containsKey(value))
               .filter(d -> d.getMap().get(value).stream().anyMatch(boundPredicate))
               .map(d -> d.getId())
               .collect(toSet());
}

Это просто читать. Вы получаете Stream<Data> из списка. Затем вы применяете фильтр, чтобы вы получали экземпляры, которые имеют значение, указанное на карте, и что есть значение, которое удовлетворяет предикату, который вы даете. Затем вы сопоставляете каждый экземпляр с соответствующим идентификатором и собираете результирующий поток в наборе.

Пример вызова:

Set<Integer> set = ids(list, "a", value -> value < 3);

который выводит:

[1, 2, 3]

Средний запрос был немного сложнее. Я закончил с другой реализацией, вы, наконец, получили Map<String, IntSummaryStatistics> в конце (которая содержит среднее значение), а также другую информацию.

Map<String, IntSummaryStatistics> stats = list.stream()
                .flatMap(d -> d.getMap().entrySet().stream())
                .collect(toMap(Map.Entry::getKey,
                               e -> e.getValue().stream().mapToInt(i -> i).summaryStatistics(),
                               (i1, i2) -> {i1.combine(i2); return i1;}));

Сначала вы получаете Stream<Data>, затем вы flatMap каждый набор flatMap каждой карты, чтобы иметь Stream<Entry<String, List<Integer>>. Теперь вы собираете этот поток в карту, для которой каждый ключ сопоставляется ключом ввода, и каждый List<Integer> сопоставляется с соответствующим значением IntSummaryStatistics. Если у вас есть два одинаковых ключа, вы комбинируете их соответствующие значения IntSummaryStatistics.

Учитывая набор данных, вы получаете Map<String, IntSummaryStatistics>

ff => IntSummaryStatistics{count=1, sum=3, min=3, average=3.000000, max=3}
bb => IntSummaryStatistics{count=1, sum=2, min=2, average=2.000000, max=2}
a => IntSummaryStatistics{count=5, sum=11, min=1, average=2.200000, max=3}
b => IntSummaryStatistics{count=1, sum=2, min=2, average=2.000000, max=2}
c => IntSummaryStatistics{count=2, sum=6, min=2, average=3.000000, max=4}
d => IntSummaryStatistics{count=1, sum=3, min=3, average=3.000000, max=3}
e => IntSummaryStatistics{count=1, sum=2, min=2, average=2.000000, max=2}

из которого вы можете легко захватить среднее.


Здесь полный рабочий пример, реализация, безусловно, может быть улучшена.

  • 0
    Спасибо, Алексис, я только что попробовал, и, похоже, он сделал то, что ожидал. Завтра я буду изучать его более подробно, поскольку в нем есть много нового, с которым я еще не знаком ...
  • 1
    Обратите внимание, что вы можете избежать одного поиска, используя getOrDefault вместо двух filter : stream().filter(d -> d.getMap().getOrDefault(id, emptyList()).stream().anyMatch(predicate)).map(...)
Показать ещё 8 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню