Random ArrayIndexOutOfBoundsException,使用流按值

时间:2015-06-23 22:04:29

标签: java java-8

在最后几天我开始“玩”一些Java 8功能,比如流(我研究了一些文档和几个例子)。

在我的应用程序中,我有一个Map,我需要获得具有最高值的三个元素(float部分)。

我尝试对我的代码进行不同的修改(以及其中一些解决方案:Sort a Map<Key, Value> by values (Java)),例如:

Map<Long, Float> great = createMapWith20Elements();
Map<Long, Float> small = great.entrySet().stream()
        .sorted(Map.Entry.<Long, Float>comparingByValue().reversed()) 
        .limit(3) 
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

但是reslt总是一样的:有时候代码工作得很好,其他它给了我一个

java.lang.ArrayIndexOutOfBoundsException: 19

在极少数情况下,超出范围的索引是18。

这种“随机”行为(18,19或正确的阐述)让我想到了一个“并行线程”问题。

我确信great地图总是有20个元素...如果我打印它们我会收到:

2,-0.5
3,0.0
4,0.0
5,0.0
6,0.0
7,-0.33333334
8,0.0
9,0.0
10,0.0
11,0.0
12,0.5
13,0.0
14,0.0
15,-0.5
18,0.0
19,0.0
21,0.0
22,0.0
23,0.0
24,0.0

我意识到有17个对象可以成为前3个......但是对我的算法来说这不是问题。

你能以某种方式帮助我吗?

由于

修改

方法createMapWith20Elements()有一个虚拟名称,可以更好地解释我的情况:我确定它会返回20个元素,因为它会使数据库读取...但它应该返回任何匹配的记录。

顺便说一下

// myIds is an ArrayList<Long>
myIds.parallelStream().forEach(e -> trust.put(e, 0f));
return trust;

替换为myIds.stream()它似乎工作正常......我无法弄清楚如何使用parallelStream写入对象(Collection而不是Stream) ,并返回对象本身(Collection),在调用函数中它可能导致这种问题。

2 个答案:

答案 0 :(得分:5)

我认为问题是方法createMapWith20Elements()。

您正在同时在地图中插入元素(可能是HashMap或TreeMap),并且HashMap和TreeMap都不会同步。因此并发插入(put方法调用)会破坏地图结构(导致地图损坏)。

如你所说:

// myIds is an ArrayList<Long>
myIds.parallelStream().forEach(e -> trust.put(e, 0f));
return trust;

有时会产生错误。但是

// myIds is an ArrayList<Long>
myIds.stream().forEach(e -> trust.put(e, 0f));
return trust;

不会产生错误。

如果要同时插入,则必须使用同步包装器。所以你的代码应该是:

// myIds is an ArrayList<Long>
Map<Long, Float> syncTrust = Collections.synchronizedSortedMap(trust);
myIds.parallelStream().forEach(e -> syncTrust.put(e, 0f));
return trust;

答案 1 :(得分:1)

问题在于逐步调整底层非同步集合的大小,我想在你的情况下你的Map结构不同步。 由于逐渐重新调整大小,在多线程上下文中未正确处理,因此可能会导致线程尝试插入超出向量当前大小范围的元素。

从“Oracle认证专业Java SE 8程序员II”一书中,Sybex:

  

对于ArrayList对象,JVM在内部管理相同类型的基本数组。作为   动态ArrayList的大小增加,周期性地需要一个新的,更大的原始数组。如果   两个线程同时触发数组调整大小,结果可能会丢失,产生   这里显示的意外值。如前面简要提到的,也将在后面讨论   章节,两个任务同时执行的意外结果是竞争条件。