在列表和jdk8上使用流,过滤器和平均值

时间:2015-03-22 00:58:15

标签: java list filter java-8 java-stream

我有这样的数据列表;

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

找到c< 4

的所有id
{2}

找到所有id的< 3

{1,2,3}

这可以很好地利用stream()和filter()??

2 个答案:

答案 0 :(得分:1)

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

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>。然后应用一个过滤器,以便只获取具有地图中给定值的实例,并且有一个值满足您给出的谓词。然后,将每个实例映射到其对应的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,但实施当然可以改进。

答案 1 :(得分: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);