最近工作中有个需求是这样的:将postgresql中的字段取出来,这个字段是以jsonb数组存储的,使用java将它转换为JSONArray后,需要将其中每个对象中属性值中包含空字符串的替换为下划线。
因为JSONArray实现了List接口,因此直接使用了parallelStream.foreach遍历。看了结果,发现有的属性中包含空字符串的并没有替换成下划线。debug后发现并行环境下,出现了线程安全问题。
然后把parallelStream去掉,但是因为在遍历过程中对集合进行了remove和add操作,会抛出并发修改异常。使用迭代器iterator遍历也不行,虽然可以实行迭代器的remove,但是add操作还是在原来集合上。
最后没有办法,只能使用CopyOnWriteArrayList这个并发容器,它适用于读多写少的场景。但我看了业务,发现写的场景也很多,但是不使用它的话,那么就需要在对集合remove和add时手动进行加锁,比如使用parallelStream.foreach的方式,然后在里面进行写的时候加锁,但是总感觉这样有点滑稽,在并行流里加锁。。。
最后贴个部分代码
CopyOnWriteArrayList<Object> copyOnWriteArrayList = new CopyOnWriteArrayList<>(valueArray);
copyOnWriteArrayList.forEach(v -> {
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(v));
if (jsonObject.containsKey("indicator") && jsonObject.get("indicator") != null) {
String indicator = JSON.toJSONString(jsonObject.get("indicator"));
if (indicator.contains(" ")) {
copyOnWriteArrayList.remove(v);
String replace = indicator.replace(" ", "_");
jsonObject.remove("indicator");
jsonObject.put("indicator", replace);
copyOnWriteArrayList.add(jsonObject);
}
}
});