list和jdk8上stream、filter和average的使用

Use of stream, filter and average on list and jdk8

我有这样的数据列表;

{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"}

我这里需要做的是像average这样的操作,或者找出字符串中某个元素小于某个值的所有id。

这里有一些例子;

平均值

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

查找所有 c<4

的 ID
{2}

查找所有 a<3

的 ID
{1,2,3}

这对 stream() 和 filter() 有用吗??

是的,您可以使用流操作来实现这一点,但我建议为该数据创建一个 class,以便每一行对应一个特定实例。在我看来,这将使您的生活更轻松。

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

话虽如此,让我们来看看如何实现它。首先,find all的实现:

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>。然后你应用一个过滤器,这样你只得到具有映射中给定值的实例,并且有一个值满足你给的谓词。然后将每个实例映射到其对应的 id,并将结果流收集到一个 Set 中。

调用示例:

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每个地图的每个条目集都有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}

从中您可以轻松获取平均值。


这里有一个完整的 working example,但实现当然可以改进。

我知道你有答案,但这是我的版本:

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