-
Bug
-
Resolution: Fixed
-
P3
-
11, 17, 21, 24
-
b25
-
generic
-
generic
-
Verified
ADDITIONAL SYSTEM INFORMATION :
The bug was introduced in Java 10.
A DESCRIPTION OF THE PROBLEM :
Since Java 10, spliterators for the ConcurrentSkipListMap were pointing to the head, which has item == null, rather than to the first element. The trySplit() method no longer worked, and always returned null. Therefore, parallel streams have not worked for ConcurrentSkipListMap and ConcurrentSkipListSet since Java 10. It worked correctly in Java 8 and 9.
REGRESSION : Last worked in version 8
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create a spliterator from a ConcurrentSkipListSet that contains some values and then call trySplit() on it. It always returns null since Java 10. Prior to that, it would return a new Spliterator.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
It should return new Spliterators, at least until the stream is exhausted.
ACTUAL -
trySplit() always returns null.
---------- BEGIN SOURCE ----------
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.stream.*;
public class ConcurrentSkipListParallelStreamTest {
public static void main(String... args) {
verifyStreamsReturnAllElements();
Collection<Integer> set = IntStream.range(0, 1_000_000)
.boxed()
.collect(Collectors.toCollection(
ConcurrentSkipListSet::new));
printResultsOfCallingTrySplit(set.spliterator());
Map<Integer, Integer> map = set.stream()
.collect(Collectors.toMap(
Function.identity(),
Function.identity(),
(a, b) -> a,
ConcurrentSkipListMap::new
));
printResultsOfCallingTrySplit(map.keySet().spliterator());
printResultsOfCallingTrySplit(map.values().spliterator());
printResultsOfCallingTrySplit(map.entrySet().spliterator());
}
private static void printResultsOfCallingTrySplit(
Spliterator<?> spliterator) {
System.out.println(spliterator);
for (int i = 0; i < 10; i++) {
Spliterator<?> split = spliterator.trySplit();
System.out.println(split);
}
System.out.println();
}
private static void verifyStreamsReturnAllElements() {
Map<Integer, Integer> map = new ConcurrentSkipListMap<>();
IntStream.rangeClosed(1, 1000)
.forEach(i -> map.put(i, i));
System.out.println("Checking sequential streams");
check(map.keySet().stream(), map.values()
.stream(), map.entrySet().stream());
System.out.println("Checking parallel streams");
check(map.keySet().parallelStream(), map.values()
.parallelStream(), map.entrySet()
.parallelStream());
}
private static void check(Stream<Integer> keyStream,
Stream<Integer> valueStream,
Stream<Map.Entry<Integer, Integer>> entryStream) {
int keySum = keyStream.mapToInt(Integer::intValue).sum();
int valueSum = valueStream.mapToInt(Integer::intValue)
.sum();
int entriesSum = entryStream
.mapToInt(entry -> entry.getKey() + entry.getValue())
.sum();
if (keySum != 500500)
throw new AssertionError("keySum = " + keySum);
if (valueSum != 500500)
throw new AssertionError("valueSum = " + valueSum);
if (entriesSum != keySum + valueSum)
throw new AssertionError("entriesSum = " + entriesSum);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Workaround is to copy the ConcurrentSkipListMap to a list and to then process that in parallel.
FREQUENCY : always
The bug was introduced in Java 10.
A DESCRIPTION OF THE PROBLEM :
Since Java 10, spliterators for the ConcurrentSkipListMap were pointing to the head, which has item == null, rather than to the first element. The trySplit() method no longer worked, and always returned null. Therefore, parallel streams have not worked for ConcurrentSkipListMap and ConcurrentSkipListSet since Java 10. It worked correctly in Java 8 and 9.
REGRESSION : Last worked in version 8
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create a spliterator from a ConcurrentSkipListSet that contains some values and then call trySplit() on it. It always returns null since Java 10. Prior to that, it would return a new Spliterator.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
It should return new Spliterators, at least until the stream is exhausted.
ACTUAL -
trySplit() always returns null.
---------- BEGIN SOURCE ----------
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.stream.*;
public class ConcurrentSkipListParallelStreamTest {
public static void main(String... args) {
verifyStreamsReturnAllElements();
Collection<Integer> set = IntStream.range(0, 1_000_000)
.boxed()
.collect(Collectors.toCollection(
ConcurrentSkipListSet::new));
printResultsOfCallingTrySplit(set.spliterator());
Map<Integer, Integer> map = set.stream()
.collect(Collectors.toMap(
Function.identity(),
Function.identity(),
(a, b) -> a,
ConcurrentSkipListMap::new
));
printResultsOfCallingTrySplit(map.keySet().spliterator());
printResultsOfCallingTrySplit(map.values().spliterator());
printResultsOfCallingTrySplit(map.entrySet().spliterator());
}
private static void printResultsOfCallingTrySplit(
Spliterator<?> spliterator) {
System.out.println(spliterator);
for (int i = 0; i < 10; i++) {
Spliterator<?> split = spliterator.trySplit();
System.out.println(split);
}
System.out.println();
}
private static void verifyStreamsReturnAllElements() {
Map<Integer, Integer> map = new ConcurrentSkipListMap<>();
IntStream.rangeClosed(1, 1000)
.forEach(i -> map.put(i, i));
System.out.println("Checking sequential streams");
check(map.keySet().stream(), map.values()
.stream(), map.entrySet().stream());
System.out.println("Checking parallel streams");
check(map.keySet().parallelStream(), map.values()
.parallelStream(), map.entrySet()
.parallelStream());
}
private static void check(Stream<Integer> keyStream,
Stream<Integer> valueStream,
Stream<Map.Entry<Integer, Integer>> entryStream) {
int keySum = keyStream.mapToInt(Integer::intValue).sum();
int valueSum = valueStream.mapToInt(Integer::intValue)
.sum();
int entriesSum = entryStream
.mapToInt(entry -> entry.getKey() + entry.getValue())
.sum();
if (keySum != 500500)
throw new AssertionError("keySum = " + keySum);
if (valueSum != 500500)
throw new AssertionError("valueSum = " + valueSum);
if (entriesSum != keySum + valueSum)
throw new AssertionError("entriesSum = " + entriesSum);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Workaround is to copy the ConcurrentSkipListMap to a list and to then process that in parallel.
FREQUENCY : always
- relates to
-
JDK-8344253 Test java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java failed
-
- Closed
-
- links to
-
Commit(master) openjdk/jdk/2b57f402
-
Review(master) openjdk/jdk/21820