使用Java流将条件映射到新对象

使用Java流将条件映射到新对象,java,java-8,java-stream,Java,Java 8,Java Stream,我有一个列表中的对象流,并希望从该流中创建新对象,以插入到集合中。但是,传入列表中的两个或多个对象可能会散列到集合中的同一个键,在这种情况下,我希望将第n个列表对象中的字符串附加到集合中已有的字符串,而不是创建新的字符串 类似这样,但以功能形式: HashSet<ClassB> mySet = new HashSet<>(); for (ClassA instanceA : classAList) { if (mySet.contains(ClassB.key(i

我有一个列表中的对象流,并希望从该流中创建新对象,以插入到集合中。但是,传入列表中的两个或多个对象可能会散列到集合中的同一个键,在这种情况下,我希望将第n个列表对象中的字符串附加到集合中已有的字符串,而不是创建新的字符串

类似这样,但以功能形式:

HashSet<ClassB> mySet = new HashSet<>();
for (ClassA instanceA : classAList) {
    if (mySet.contains(ClassB.key(instanceA))) { //static method call to find the key
        mySet.get(instanceA).appendFieldA(instanceA.getFieldA());
    } else {
        mySet.add(new ClassB(instanceA));
    }
}
return mySet;
在功能形式中,我想创建如下内容:

List classAList = new ArrayList<>();
classAList.stream()
.map(instanceA -> new ClassB(instanceA))
.collect(Collectors.toSet());
List classAList = new ArrayList<>();
classAList.stream()
.collect(Collectors.groupingBy(instanceA -> ClassB.key(instanceB)))
.entrySet()
.map(entry -> entry.getValue().stream()
   .map(instanceA -> new ClassB(instanceA))
   .reduce(null, (a,b) -> a.appendFieldA(b)))
.collect(Collectors.toSet());

当然,这忽略了hashmap,我不能将多个ClassA实例中的字段合并到同一个ClassB中。我不知道怎么把它放进去。我是否需要忽略映射调用并创建一个自定义收集器来执行此任务?似乎有不止一种方法可以做到这一点,但我对Streams是新手。

您可以将条目分组到一个映射中,该映射将散列键映射到元素列表,然后再次调用map将该映射转换为您要查找的集。大概是这样的:

List classAList = new ArrayList<>();
classAList.stream()
.map(instanceA -> new ClassB(instanceA))
.collect(Collectors.toSet());
List classAList = new ArrayList<>();
classAList.stream()
.collect(Collectors.groupingBy(instanceA -> ClassB.key(instanceB)))
.entrySet()
.map(entry -> entry.getValue().stream()
   .map(instanceA -> new ClassB(instanceA))
   .reduce(null, (a,b) -> a.appendFieldA(b)))
.collect(Collectors.toSet());

您可以将这些条目分组到一个映射中,该映射将散列键映射到元素列表,然后再次调用map将该映射转换为您要查找的集合。大概是这样的:

List classAList = new ArrayList<>();
classAList.stream()
.map(instanceA -> new ClassB(instanceA))
.collect(Collectors.toSet());
List classAList = new ArrayList<>();
classAList.stream()
.collect(Collectors.groupingBy(instanceA -> ClassB.key(instanceB)))
.entrySet()
.map(entry -> entry.getValue().stream()
   .map(instanceA -> new ClassB(instanceA))
   .reduce(null, (a,b) -> a.appendFieldA(b)))
.collect(Collectors.toSet());

很难理解您真正想要的是什么,因为您的代码示例根本不起作用。问题是集合的工作方式与映射不同,您不能要求它提供包含的等效对象。除此之外,您正在为包含…和获取…调用使用不同的对象。此外,还不清楚ClassB.keyinstanceA和new ClassBinInstanceA之间的区别

让我们尝试重新定义它:

假设我们有一个键类型键和一个方法键.keyinstanceA来定义组候选项。然后我们有一个ClassB,它是结果类型,通过新的ClassBinstanceA为单个或主ClassA实例创建,有一个.appendFieldA…方法在合并两个组成员时接收另一个ClassA实例的值。然后,Java 8之前的原始代码将如下所示:

HashMap<Key, ClassB> myMap = new HashMap<>();
for(ClassA instanceA: classAList) {
    Key key=Key.key(instanceA);
    if(myMap.containsKey(key)) {
        myMap.get(key).appendFieldA(instanceA.getFieldA());
    } else {
        myMap.put(key, new ClassB(instanceA));
    }
}
请注意,当Key和ClassB在代码中看起来是相同的,但您可能会问自己,您是否真的需要这两个实例,通过.keyinstanceA创建的实例和通过new ClassBinInstancea创建的实例

这可以通过Java 8 API简化为:

for(ClassA instanceA: classAList) {
    myMap.compute(Key.key(instanceA), (k,b)-> {
        if(b==null) b=new ClassB(instanceA);
        else b.appendFieldA(instanceA.getFieldA());
        return b;
    });
}
或者,如果您想让它看起来更加功能时尚:

classAList.forEach(instanceA ->
    myMap.compute(Key.key(instanceA), (k,b)-> {
        if(b==null) b=new ClassB(instanceA);
        else b.appendFieldA(instanceA.getFieldA());
        return b;
    })
);
对于流解决方案,有一个问题,合并函数将获得两个相同类型的实例,这里是ClassB,并且不能像上面的计算解决方案那样通过周围的上下文访问ClassA实例。对于流解决方案,我们需要ClassB中的一个方法,它返回第一个ClassA实例,我们将其传递给它的构造函数,比如getFirstInstanceA。然后我们可以使用:

Map<Key, ClassB> myMap = classAList.stream()
    .collect(Collectors.toMap(Key::key, ClassB::new, (b1,b2)->{
        b1.appendFieldA(b2.getFirstInstanceA().getFieldA());
        return b1;
    }));

很难理解您真正想要的是什么,因为您的代码示例根本不起作用。问题是集合的工作方式与映射不同,您不能要求它提供包含的等效对象。除此之外,您正在为包含…和获取…调用使用不同的对象。此外,还不清楚ClassB.keyinstanceA和new ClassBinInstanceA之间的区别

让我们尝试重新定义它:

假设我们有一个键类型键和一个方法键.keyinstanceA来定义组候选项。然后我们有一个ClassB,它是结果类型,通过新的ClassBinstanceA为单个或主ClassA实例创建,有一个.appendFieldA…方法在合并两个组成员时接收另一个ClassA实例的值。然后,Java 8之前的原始代码将如下所示:

HashMap<Key, ClassB> myMap = new HashMap<>();
for(ClassA instanceA: classAList) {
    Key key=Key.key(instanceA);
    if(myMap.containsKey(key)) {
        myMap.get(key).appendFieldA(instanceA.getFieldA());
    } else {
        myMap.put(key, new ClassB(instanceA));
    }
}
请注意,当Key和ClassB在代码中看起来是相同的,但您可能会问自己,您是否真的需要这两个实例,通过.keyinstanceA创建的实例和通过new ClassBinInstancea创建的实例

这可以通过Java 8 API简化为:

for(ClassA instanceA: classAList) {
    myMap.compute(Key.key(instanceA), (k,b)-> {
        if(b==null) b=new ClassB(instanceA);
        else b.appendFieldA(instanceA.getFieldA());
        return b;
    });
}
或者,如果您想让它看起来更加功能时尚:

classAList.forEach(instanceA ->
    myMap.compute(Key.key(instanceA), (k,b)-> {
        if(b==null) b=new ClassB(instanceA);
        else b.appendFieldA(instanceA.getFieldA());
        return b;
    })
);
对于流解决方案,有一个问题,合并函数将获得两个相同类型的实例,这里是ClassB,并且不能像上面的计算解决方案那样通过周围的上下文访问ClassA实例。对于流解决方案,我们需要ClassB中的一个方法,它返回第一个ClassA实例,我们将其传递给它的构造函数,比如getFirstInstanceA。然后我们可以使用:

Map<Key, ClassB> myMap = classAList.stream()
    .collect(Collectors.toMap(Key::key, ClassB::new, (b1,b2)->{
        b1.appendFieldA(b2.getFirstInstanceA().getFieldA());
        return b1;
    }));

整个想法的问题是,如果行为是基于状态的,那么冲突的散列对象是否已经在集合中,那么你就不再在功能上做事情了。mySet.getinstanceA应该是什么意思?我认为你混淆了地图和集合。你能粘贴你的实际代码吗?好吧,整个想法的问题是,如果行为是基于状态的,那么冲突的哈希对象是否已经在集合中,那么你就不再在功能上做事情了。mySet.getinstanceA应该是什么意思?我认为你混淆了地图和集合。你能粘贴你的实际代码吗
pendFieldA不接受ClassB类型的对象。此外,将null作为标识值传递给reduce意味着当前实现可能会尝试在该null引用上调用appendFieldA。我没有意识到问题是将classb放在集合中,而是在classa上调用appendFieldA。为了使其工作,您需要某种类型的标识classaBut appendFieldA不接受ClassB类型的对象。此外,将null作为标识值传递给reduce意味着当前实现可能会尝试在该null引用上调用appendFieldA。我没有意识到问题是将classb放在集合中,而是在classa上调用appendFieldA。为了让它起作用,你需要某种身份分类——谢谢你,这是完全正确的,就像你对我的例子出错的地方的解释一样。在我努力简化问题陈述的过程中,我把原来的代码示例搞砸了;我向所有注意到原始代码没有太多意义的人表示歉意。谢谢,这是完全正确的,正如您对我的示例出错之处的解释一样。在我努力简化问题陈述的过程中,我把原来的代码示例搞砸了;我向所有注意到原始代码没有多大意义的人道歉。