- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我试图理解我在 Streams 文档中发现的警告。我已经养成了使用 forEach() 作为通用迭代器的习惯。这导致我编写了这种类型的代码:
public class FooCache {
private static Map<Integer, Integer> sortOrderCache = new ConcurrentHashMap<>();
private static Map<Integer, String> codeNameCache = new ConcurrentHashMap<>();
public static void populateCache() {
List<Foo> myThings = getThings();
myThings.forEach(thing -> {
sortOrderCache.put(thing.getId(), thing.getSortOrder());
codeNameCache.put(thing.getId(), thing.getCodeName())
});
}
}
这是一个简单的例子。我知道这段代码违反了 Oracle 对有状态的 lamda 和副作用的警告。但我不明白为什么会出现此警告。
运行此代码时,它的行为似乎符合预期。那么我该如何打破它来证明为什么这是个坏主意呢?
在某种程度上,我读到了这个:
If executed in parallel, the non-thread-safety of ArrayList would cause incorrect results, and adding needed synchronization would cause contention, undermining the benefit of parallelism.
但是谁能更清楚地帮助我理解警告?
最佳答案
来自 Javadoc:
Note also that attempting to access mutable state from behavioral parameters presents you with a bad choice with respect to safety and performance; if you do not synchronize access to that state, you have a data race and therefore your code is broken, but if you do synchronize access to that state, you risk having contention undermine the parallelism you are seeking to benefit from. The best approach is to avoid stateful behavioral parameters to stream operations entirely; there is usually a way to restructure the stream pipeline to avoid statefulness.
这里的问题是,如果你访问一个可变状态,你就会失去两方面:
Stream
试图最小化的同步ConcurrentHashMap
,这会产生费用)。现在,在您的示例中,这里有几点:
Stream
和多线程流,你需要像myThings.parralelStream()
那样使用parralelStream()
;就目前而言,java.util.Collection
提供的forEach
方法很简单for each
。HashMap
作为 static
成员并对其进行变异。 HashMap
不是线程安全的;您需要使用 ConcurrentHashMap
。在 lambda 中,在 Stream
的情况下,您不得改变流的源:
myThings.stream().forEach(thing -> myThings.remove(thing));
这可能有效(但我怀疑它会抛出 ConcurrentModificationException
),但这可能无效:
myThings.parallelStream().forEach(thing -> myThings.remove(thing));
那是因为 ArrayList
不是线程安全的。
如果您使用同步 View (Collections.synchronizedList
),那么您将获得性能,因为您在每次访问时都进行了同步。
在您的示例中,您宁愿使用:
sortOrderCache = myThings.stream()
.collect(Collectors.groupingBy(
Thing::getId, Thing::getSortOrder);
codeNameCache= myThings.stream()
.collect(Collectors.groupingBy(
Thing::getId, Thing::getCodeName);
终结器(此处为 groupingBy
)执行您正在执行的工作,并且可能会按顺序调用(我的意思是,Stream 可能会跨多个线程拆分,终结器可能会被调用多次(在不同的线程中)然后它可能需要合并。
顺便说一下,您最终可能会删除 codeNameCache
/sortOrderCache
并简单地存储 id->Thing 映射。
关于java - Java 8 Streams 中副作用的危险是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47041144/
这2个有什么区别?一个使用 SideEffect,另一个不使用。 “每次成功重组都会调用 SideEffect”,但如果没有 SideEffect,它也会在每次重组时运行。 @Composable f
我在 DOM 元素引用方面遇到了一些问题,我想我已经追踪到它与更新 innerHTML 有关。 在这个例子中,在第一次警告时,两个变量引用同一个元素,正如预期的那样。奇怪的是,在更新父元素(body)
如果有人问过这个问题,请原谅我,但我似乎找不到它。 我正在尝试创建一个数组并反转它(不使用反转)这段代码完美运行: function reverseArrayInPlace(array) { fo
如果 reflector 是正确的(我倾向于相信它是正确的),这就是 Any() 的实现: public static bool Any(this IEnumerable source) {
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
是否可以用 LINQ 中的 lambda 表达式替换 foreach 循环 (.Select))? List l = {1, 2, 3, 4, 5}; foreach (int i in l)
我在一本书上读到以下说法: n = ((i++) > (j)?(i++):(j)); 书上说假设i>j,n有一个意想不到的值,i增加了两次。 我不明白为什么n在这句话之后有一个期望值。 我读了很多关于
我对更改 LD_LIBRARY_PATH 有奇怪的副作用。 当我附加一个包含库的路径时,例如: LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/my_path/lib 然后,一切都
有人能告诉我下面一行中“副作用”的含义吗? If you're calling an EL function that doesn't return anything, then you're cal
是否有为包含副作用的 Java/JVM 语言方法编写 javadoc 的标准或最佳实践? 我定义了一个 void 方法,它修改了方法参数之一,但不知道如何记录实际返回值(因为没有实际返回)。 /**
我正在学习副作用和纯函数。我知道纯函数没有副作用,对于相同的参数,它们的返回值是相同的。我想知道 C 函数 strcmp() 是否是纯函数。我相信它是纯粹的,因为给定相同的两个字符串作为参数,结果将始
我正在尝试创建佛罗里达州的点密度图。虽然我知道 Highmaps 不支持带有 map 点的颜色轴。我扩展了它并且它有效,但它带来了副作用。当我单击图例中的某一类别时,不会发生隐藏。例如,如果我单击“>
我在 CS50 中研究 PSET 4,似乎遇到了 sprintf 更改不相关变量的问题。我只给出了没有揭示我的解决方案的代码...... #include #include #include t
我已经实现了这样的 UnaryOperation struct Converter { Converter( std::size_t value ): value_( valu
使用点符号调用自定义 getter 是否有副作用? 我一直在通过点符号在 Objective-C 中使用合成的 getter,即 tree.fruitnumber 返回树中果实的数量。我必须自定义 s
我无法理解页面 https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special/void 中的这一段: This ope
我有一个在 IIS7 下运行的 Web 应用程序。我将全局变量存储在一个带有静态变量的类中。该类称为 SessionVariables 并且在其中例如我有以下内容: public class Sess
运行命令时 ng-packagr -p ng-package.json 我得到以下输出 Building Angular library - - - skipped 8 lines - - - Sid
我想模拟一个 OverflowError 因为我想在引发异常之后测试变量的值。但是,我不知道如何使用我正在使用的库复制 OverflowError。我在此特定测试中使用的库是 pysolar.sola
当我尝试在可变 Map 中插入一个元素时,我希望这个元素插入到我的 Map 而不是返回 Map(如 PF,不可变对象(immutable对象) ecc ...)出于这个原因,我使用了可变集合,但在我的
我是一名优秀的程序员,十分优秀!