用对键将三元组列表分组到映射中

时间:2018-07-12 15:06:41

标签: java java-8

我有一个triples

的列表
List<Triple<String, String, String>> triplets; 

我想分组成这样的地图

Map<Pair<String, String>, String> mapping;

地图的value处的位置是三元组的第三个元素。并且如果使用相同的key,则它应覆盖剩余的第三个值。

例如

def triples = [ {a, b, c} ; {a, d, e} ; {a, b, f } ]
// grouping
def map = [ {a,b} : c ; {a, d} : e ]

如何在流中使用Java 8及其grouping

4 个答案:

答案 0 :(得分:4)

这应该可以解决问题:

Map<Pair<String, String>, String> result = triplets.stream()
    .collect(
        Collectors.toMap(
            t -> new Pair(t.getOne(), t.getTwo()),
            Triple::getThree,
            (v1, v2) -> v2
        )
    );

部分配对类的示例:

public class Pair<T, U> {
    //...

    @Override
    public int hashCode() {
        return one.hashCode() + two.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Pair))
            return false;
        Pair p = (Pair) obj;
        return p.one.equals(one) && p.two.equals(two);
    }
}

HashMap类使用equals方法来唯一地标识关键对象。因此,您首先需要重写equalshashcode方法以显示Pair类的Map对象的逻辑相等性。

然后回到小溪和lambda。对于每个三元组,请使用Collectors.toMap,将Pair对象作为键,并将Triplet的其他剩余值用作值。然后提供mergeFunction来处理键冲突。在您的情况下,您需要保留先前的值,而丢弃新的值。这就是您要做的全部。

更新

我已根据以下注释更新了合并功能。

答案 1 :(得分:1)

如果要使用Collectors.groupingBy,可以将其与下游mappingreducing Collectors组合:

@Data @AllArgsConstructor
class Triple {
    String x, y ,z;
}
@Data @AllArgsConstructor
class Pair {
    String x, y;
}

List<Triple> lst = List.of(new Triple("a", "b", "c"),
                           new Triple("a", "d", "e"),
                           new Triple("a", "b", "f"));

Map<Pair, String> map = lst.stream()
        .collect(Collectors.groupingBy(t -> new Pair(t.x, t.y),
                 Collectors.mapping(t -> t.z, 
                 Collectors.reducing(null, (x,y) -> y))));
// {Pair(x=a, y=b)=f, Pair(x=a, y=d)=e}

您也可以将(x,y) -> y更改为(x,y) -> x,以保持第一次出现。

答案 2 :(得分:0)

这里可能的解决方案

        String[] l1=new String[]{"a", "b", "c"};
    String[] l2={"a", "d", "e"};

    String[] l3={"a", "b", "c"};
    List<String[]> t=new ArrayList<>();
    t.add(l1);

    t.add(l2);
    t.add(l3);

    Map<String[],String> m=t.stream().
    collect(Collectors.toMap(x->new String[]{x[0],x[1]}, x->x[2],(x,y)->x));

但是取决于 Triple 的类型,如果它覆盖等于,则不会重复,否则将在映射的键中重复

答案 3 :(得分:0)

使用Map::merge

Map<Pair<String, String>, String> result = new HashMap<>();
triplets.forEach(e -> result.merge(new Pair<>(e.getOne(), e.getThree()), e.getThree(), (o, n) -> o));

这会将triplets列表的元素插入到映射中,其中的键是一对,分别表示Triple的第一个和第二个值,映射的值是{的第三个值{1}}。

在发生键冲突的情况下,我们使用重映射函数Triple来维持旧的((o, n) -> o)值。