修改流时 putIfAbsent() 出现问题

问题描述 投票:0回答:2

请帮我找出以下方法有什么问题以及如何解决。该方法采用

Person
对象流和 Map,其中 String 值(任务名称)作为键,int 值(标记)。该方法的目的是检查流是否包含
allHistoryTasks
变量中的特定任务,以及是否适用于该 Map。
putIfAbsentmethod(taskName, 0)
确保所有任务都出现在地图中(目的是为了稍后计算平均分)。

当我运行测试时,出现

UnsupportedOperationException
错误。当我评论从
if
语句到
forEach
(第 1、2、3、4 行)的行时,测试运行良好。我是Java新手,已经在这个问题上花了好几天了,但仍然无法解决。请告诉我这里出了什么问题。

private Set<String> allHistoryTasks = Set.of("Phalanxing", "Shieldwalling", "Tercioing", "Wedging");
private String[] historyTasks = allHistoryTasks.toArray(new String[0]);

public Map<Person, Map<String, Integer>> addHistoryIfPresent(Stream<CourseResult> stream) {
    return stream.collect(Collectors.toMap(
            CourseResult::getPerson,
            x -> {
                if (allHistoryTasks.containsAll(x.getTaskResults().keySet()))    //1
                    IntStream.range(0, allHistoryTasks.size())                   //2
                            .parallel()                                          //3
                            .forEach(i -> x.getTaskResults().putIfAbsent(historyTasks[i], 0));  //4
                return x.getTaskResults();
            }
    ));
}

自定义类和线程报告

java java-stream
2个回答
1
投票

x -> {}
块是“值映射器”。它应该将流的元素转换为给定地图的值。

您有一个

CourseResult
对象流,并且想要一个
Map<Person, Map<String, Integer>>, so this function turns a 
CourseResult
object into a
Map`。

你可以通过改变事物来做到这一点,这是一个非常严重的问题。您的流函数不应该有任何副作用。幸运的是,

CourseResult
的作者远远领先于你,阻止了你犯这个错误。您正在对课程结果对象调用
.getTaskResults()
,然后尝试修改它。您不能这样做,因为
getTaskResults()
方法返回无法修改的地图。

大概,您想要克隆该地图,并修复克隆。你是怎样做的?好吧,你告诉我,API 不清楚。您可以简单地创建一个新的

ImmutableMap.builder()
,循环遍历您想要循环的任何内容,依此类推。从您的代码来看,不太清楚您想要什么最终地图。

还要注意,您在不知道自己在做什么的情况下使用权力 - 您有一个并行流,然后通过它进行 forEaching,改变同一个变量,这是您绝对不能做的:这会导致错误,其中结果为一个操作取决于一次邪恶的硬币翻转,从某种意义上说,即使您重新运行测试一百万次,但明天就会失败,它今天也可以正常工作。另外,使用

parallel()
进行此类讨论是近乎疯狂的 - 假设底层流实现实际上是并行化的(
.parallel()
是一个提示,而不是需求),它只会减慢一切。
allHistoryTasks
很小。这不是并行性的用途。


0
投票

这可能是您问题的答案。

Set.of
方法不会返回可变集。所以你需要像这样声明一个可变集来避免这个问题。

private Set<String> allHistoryTasks = new HashSet<>(Arrays.asList("Phalanxing", "Shieldwalling", "Tercioing", "Wedging"));
private String[] historyTasks = allHistoryTasks.toArray(new String[0]);

public Map<Person, Map<String, Integer>> addHistoryIfPresent(Stream<CourseResult> stream) {
    return stream.collect(Collectors.toMap(
            CourseResult::getPerson,
            x -> {
                if (allHistoryTasks.containsAll(x.getTaskResults().keySet()))    //1
                    IntStream.range(0, allHistoryTasks.size())                   //2
                            .parallel()                                          //3
                            .forEach(i -> x.getTaskResults().putIfAbsent(historyTasks[i], 0));  //4
                return x.getTaskResults();
            }
    ));
}
© www.soinside.com 2019 - 2024. All rights reserved.