8153293: Preserve SORTED and DISTINCT characteristics for boxed() and asLongStream() operations
authortvaleev
Fri, 29 Apr 2016 16:57:57 -0700
changeset 37721 1258d14cf2cc
parent 37720 45cd7cc65382
child 37722 d8cbae550f3b
8153293: Preserve SORTED and DISTINCT characteristics for boxed() and asLongStream() operations Reviewed-by: psandoz
jdk/src/java.base/share/classes/java/util/stream/DoublePipeline.java
jdk/src/java.base/share/classes/java/util/stream/IntPipeline.java
jdk/src/java.base/share/classes/java/util/stream/LongPipeline.java
jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/DoublePrimitiveOpsTests.java
jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/IntPrimitiveOpsTests.java
jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/LongPrimitiveOpsTests.java
--- a/jdk/src/java.base/share/classes/java/util/stream/DoublePipeline.java	Sat Apr 30 00:30:31 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/util/stream/DoublePipeline.java	Fri Apr 29 16:57:57 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -167,6 +167,19 @@
         return Nodes.doubleBuilder(exactSizeIfKnown);
     }
 
+    private <U> Stream<U> mapToObj(DoubleFunction<? extends U> mapper, int opFlags) {
+        return new ReferencePipeline.StatelessOp<Double, U>(this, StreamShape.DOUBLE_VALUE, opFlags) {
+            @Override
+            Sink<Double> opWrapSink(int flags, Sink<U> sink) {
+                return new Sink.ChainedDouble<U>(sink) {
+                    @Override
+                    public void accept(double t) {
+                        downstream.accept(mapper.apply(t));
+                    }
+                };
+            }
+        };
+    }
 
     // DoubleStream
 
@@ -184,7 +197,7 @@
 
     @Override
     public final Stream<Double> boxed() {
-        return mapToObj(Double::valueOf);
+        return mapToObj(Double::valueOf, 0);
     }
 
     @Override
@@ -207,18 +220,7 @@
     @Override
     public final <U> Stream<U> mapToObj(DoubleFunction<? extends U> mapper) {
         Objects.requireNonNull(mapper);
-        return new ReferencePipeline.StatelessOp<Double, U>(this, StreamShape.DOUBLE_VALUE,
-                                                            StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
-            @Override
-            Sink<Double> opWrapSink(int flags, Sink<U> sink) {
-                return new Sink.ChainedDouble<U>(sink) {
-                    @Override
-                    public void accept(double t) {
-                        downstream.accept(mapper.apply(t));
-                    }
-                };
-            }
-        };
+        return mapToObj(mapper, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT);
     }
 
     @Override
--- a/jdk/src/java.base/share/classes/java/util/stream/IntPipeline.java	Sat Apr 30 00:30:31 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/util/stream/IntPipeline.java	Fri Apr 29 16:57:57 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -170,6 +170,19 @@
         return Nodes.intBuilder(exactSizeIfKnown);
     }
 
+    private <U> Stream<U> mapToObj(IntFunction<? extends U> mapper, int opFlags) {
+        return new ReferencePipeline.StatelessOp<Integer, U>(this, StreamShape.INT_VALUE, opFlags) {
+            @Override
+            Sink<Integer> opWrapSink(int flags, Sink<U> sink) {
+                return new Sink.ChainedInt<U>(sink) {
+                    @Override
+                    public void accept(int t) {
+                        downstream.accept(mapper.apply(t));
+                    }
+                };
+            }
+        };
+    }
 
     // IntStream
 
@@ -187,8 +200,7 @@
 
     @Override
     public final LongStream asLongStream() {
-        return new LongPipeline.StatelessOp<Integer>(this, StreamShape.INT_VALUE,
-                                                     StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
+        return new LongPipeline.StatelessOp<Integer>(this, StreamShape.INT_VALUE, 0) {
             @Override
             Sink<Integer> opWrapSink(int flags, Sink<Long> sink) {
                 return new Sink.ChainedInt<Long>(sink) {
@@ -203,8 +215,7 @@
 
     @Override
     public final DoubleStream asDoubleStream() {
-        return new DoublePipeline.StatelessOp<Integer>(this, StreamShape.INT_VALUE,
-                                                       StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
+        return new DoublePipeline.StatelessOp<Integer>(this, StreamShape.INT_VALUE, 0) {
             @Override
             Sink<Integer> opWrapSink(int flags, Sink<Double> sink) {
                 return new Sink.ChainedInt<Double>(sink) {
@@ -219,7 +230,7 @@
 
     @Override
     public final Stream<Integer> boxed() {
-        return mapToObj(Integer::valueOf);
+        return mapToObj(Integer::valueOf, 0);
     }
 
     @Override
@@ -242,18 +253,7 @@
     @Override
     public final <U> Stream<U> mapToObj(IntFunction<? extends U> mapper) {
         Objects.requireNonNull(mapper);
-        return new ReferencePipeline.StatelessOp<Integer, U>(this, StreamShape.INT_VALUE,
-                                                             StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
-            @Override
-            Sink<Integer> opWrapSink(int flags, Sink<U> sink) {
-                return new Sink.ChainedInt<U>(sink) {
-                    @Override
-                    public void accept(int t) {
-                        downstream.accept(mapper.apply(t));
-                    }
-                };
-            }
-        };
+        return mapToObj(mapper, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT);
     }
 
     @Override
--- a/jdk/src/java.base/share/classes/java/util/stream/LongPipeline.java	Sat Apr 30 00:30:31 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/util/stream/LongPipeline.java	Fri Apr 29 16:57:57 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -167,6 +167,19 @@
         return Nodes.longBuilder(exactSizeIfKnown);
     }
 
+    private <U> Stream<U> mapToObj(LongFunction<? extends U> mapper, int opFlags) {
+        return new ReferencePipeline.StatelessOp<Long, U>(this, StreamShape.LONG_VALUE, opFlags) {
+            @Override
+            Sink<Long> opWrapSink(int flags, Sink<U> sink) {
+                return new Sink.ChainedLong<U>(sink) {
+                    @Override
+                    public void accept(long t) {
+                        downstream.accept(mapper.apply(t));
+                    }
+                };
+            }
+        };
+    }
 
     // LongStream
 
@@ -184,8 +197,7 @@
 
     @Override
     public final DoubleStream asDoubleStream() {
-        return new DoublePipeline.StatelessOp<Long>(this, StreamShape.LONG_VALUE,
-                                                    StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
+        return new DoublePipeline.StatelessOp<Long>(this, StreamShape.LONG_VALUE, StreamOpFlag.NOT_DISTINCT) {
             @Override
             Sink<Long> opWrapSink(int flags, Sink<Double> sink) {
                 return new Sink.ChainedLong<Double>(sink) {
@@ -200,7 +212,7 @@
 
     @Override
     public final Stream<Long> boxed() {
-        return mapToObj(Long::valueOf);
+        return mapToObj(Long::valueOf, 0);
     }
 
     @Override
@@ -223,18 +235,7 @@
     @Override
     public final <U> Stream<U> mapToObj(LongFunction<? extends U> mapper) {
         Objects.requireNonNull(mapper);
-        return new ReferencePipeline.StatelessOp<Long, U>(this, StreamShape.LONG_VALUE,
-                                                          StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
-            @Override
-            Sink<Long> opWrapSink(int flags, Sink<U> sink) {
-                return new Sink.ChainedLong<U>(sink) {
-                    @Override
-                    public void accept(long t) {
-                        downstream.accept(mapper.apply(t));
-                    }
-                };
-            }
-        };
+        return mapToObj(mapper, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT);
     }
 
     @Override
--- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/DoublePrimitiveOpsTests.java	Sat Apr 30 00:30:31 2016 +0100
+++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/DoublePrimitiveOpsTests.java	Fri Apr 29 16:57:57 2016 -0700
@@ -27,11 +27,18 @@
 
 import java.util.Arrays;
 import java.util.Random;
+import java.util.Spliterator;
 import java.util.stream.DoubleStream;
 import java.util.stream.LongStream;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
 
+/**
+ * @test
+ * @bug 8153293
+ */
 @Test
 public class DoublePrimitiveOpsTests {
 
@@ -42,6 +49,13 @@
         assertEquals(sum, 1.0 + 2.0 + 3.0 + 4.0 + 5.0);
     }
 
+    public void testFlags() {
+        assertTrue(LongStream.range(1, 10).asDoubleStream().boxed().spliterator()
+                      .hasCharacteristics(Spliterator.SORTED));
+        assertFalse(DoubleStream.of(1, 10).boxed().spliterator()
+                      .hasCharacteristics(Spliterator.SORTED));
+    }
+
     public void testToArray() {
         {
             double[] array =  LongStream.range(1, 10).asDoubleStream().map(i -> i * 2).toArray();
@@ -72,6 +86,22 @@
         }
     }
 
+    public void testSortDistinct() {
+        {
+            double[] range = LongStream.range(0, 10).asDoubleStream().toArray();
+
+            assertEquals(LongStream.range(0, 10).asDoubleStream().sorted().distinct().toArray(), range);
+            assertEquals(LongStream.range(0, 10).asDoubleStream().parallel().sorted().distinct().toArray(), range);
+
+            double[] data = {5, 3, 1, 1, 5, Double.NaN, 3, 9, Double.POSITIVE_INFINITY,
+                             Double.NEGATIVE_INFINITY, 2, 9, 1, 0, 8, Double.NaN, -0.0};
+            double[] expected = {Double.NEGATIVE_INFINITY, -0.0, 0, 1, 2, 3, 5, 8, 9,
+                                 Double.POSITIVE_INFINITY, Double.NaN};
+            assertEquals(DoubleStream.of(data).sorted().distinct().toArray(), expected);
+            assertEquals(DoubleStream.of(data).parallel().sorted().distinct().toArray(), expected);
+        }
+    }
+
     public void testSortSort() {
         Random r = new Random();
 
--- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/IntPrimitiveOpsTests.java	Sat Apr 30 00:30:31 2016 +0100
+++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/IntPrimitiveOpsTests.java	Fri Apr 29 16:57:57 2016 -0700
@@ -28,13 +28,21 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Random;
+import java.util.Spliterator;
+import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.IntConsumer;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
 
+/**
+ * @test
+ * @bug 8153293
+ */
 @Test
 public class IntPrimitiveOpsTests {
 
@@ -85,6 +93,29 @@
         assertEquals(sum, 15);
     }
 
+    public void testFlags() {
+        assertTrue(IntStream.range(1, 10).boxed().spliterator()
+                      .hasCharacteristics(Spliterator.SORTED | Spliterator.DISTINCT));
+        assertFalse(IntStream.of(1, 10).boxed().spliterator()
+                      .hasCharacteristics(Spliterator.SORTED));
+        assertFalse(IntStream.of(1, 10).boxed().spliterator()
+                      .hasCharacteristics(Spliterator.DISTINCT));
+
+        assertTrue(IntStream.range(1, 10).asLongStream().spliterator()
+                      .hasCharacteristics(Spliterator.SORTED | Spliterator.DISTINCT));
+        assertFalse(IntStream.of(1, 10).asLongStream().spliterator()
+                      .hasCharacteristics(Spliterator.SORTED));
+        assertFalse(IntStream.of(1, 10).asLongStream().spliterator()
+                      .hasCharacteristics(Spliterator.DISTINCT));
+
+        assertTrue(IntStream.range(1, 10).asDoubleStream().spliterator()
+                      .hasCharacteristics(Spliterator.SORTED | Spliterator.DISTINCT));
+        assertFalse(IntStream.of(1, 10).asDoubleStream().spliterator()
+                      .hasCharacteristics(Spliterator.SORTED));
+        assertFalse(IntStream.of(1, 10).asDoubleStream().spliterator()
+                      .hasCharacteristics(Spliterator.DISTINCT));
+    }
+
     public void testToArray() {
         {
             int[] array =  IntStream.range(1, 10).map(i -> i * 2).toArray();
@@ -115,6 +146,35 @@
         }
     }
 
+    public void testSortDistinct() {
+        {
+            int[] range = IntStream.range(0, 10).toArray();
+
+            assertEquals(IntStream.range(0, 10).sorted().distinct().toArray(), range);
+            assertEquals(IntStream.range(0, 10).parallel().sorted().distinct().toArray(), range);
+
+            int[] data = {5, 3, 1, 1, 5, 3, 9, 2, 9, 1, 0, 8};
+            int[] expected = {0, 1, 2, 3, 5, 8, 9};
+            assertEquals(IntStream.of(data).sorted().distinct().toArray(), expected);
+            assertEquals(IntStream.of(data).parallel().sorted().distinct().toArray(), expected);
+        }
+
+        {
+            int[] input = new Random().ints(100, -10, 10).map(x -> x+Integer.MAX_VALUE).toArray();
+            TreeSet<Long> longs = new TreeSet<>();
+            for(int i : input) longs.add((long)i);
+            long[] expectedLongs = longs.stream().mapToLong(Long::longValue).toArray();
+            assertEquals(IntStream.of(input).sorted().asLongStream().sorted().distinct().toArray(),
+                         expectedLongs);
+
+            TreeSet<Double> doubles = new TreeSet<>();
+            for(int i : input) doubles.add((double)i);
+            double[] expectedDoubles = doubles.stream().mapToDouble(Double::doubleValue).toArray();
+            assertEquals(IntStream.of(input).sorted().distinct().asDoubleStream()
+                         .sorted().distinct().toArray(), expectedDoubles);
+        }
+    }
+
     public void testSortSort() {
         Random r = new Random();
 
--- a/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/LongPrimitiveOpsTests.java	Sat Apr 30 00:30:31 2016 +0100
+++ b/jdk/test/java/util/stream/test/org/openjdk/tests/java/util/stream/LongPrimitiveOpsTests.java	Fri Apr 29 16:57:57 2016 -0700
@@ -28,13 +28,21 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Random;
+import java.util.Spliterator;
+import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.LongConsumer;
 import java.util.stream.Collectors;
 import java.util.stream.LongStream;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
 
+/**
+ * @test
+ * @bug 8153293
+ */
 @Test
 public class LongPrimitiveOpsTests {
 
@@ -85,6 +93,22 @@
         assertEquals(sum, 15);
     }
 
+    public void testFlags() {
+        assertTrue(LongStream.range(1, 10).boxed().spliterator()
+                      .hasCharacteristics(Spliterator.SORTED | Spliterator.DISTINCT));
+        assertFalse(LongStream.of(1, 10).boxed().spliterator()
+                      .hasCharacteristics(Spliterator.SORTED));
+        assertFalse(LongStream.of(1, 10).boxed().spliterator()
+                      .hasCharacteristics(Spliterator.DISTINCT));
+
+        assertTrue(LongStream.range(1, 10).asDoubleStream().spliterator()
+                      .hasCharacteristics(Spliterator.SORTED));
+        assertFalse(LongStream.range(1, 10).asDoubleStream().spliterator()
+                      .hasCharacteristics(Spliterator.DISTINCT));
+        assertFalse(LongStream.of(1, 10).asDoubleStream().spliterator()
+                      .hasCharacteristics(Spliterator.SORTED));
+    }
+
     public void testToArray() {
         {
             long[] array =  LongStream.range(1, 10).map(i -> i * 2).toArray();
@@ -115,6 +139,30 @@
         }
     }
 
+    public void testSortDistinct() {
+        {
+            long[] range = LongStream.range(0, 10).toArray();
+
+            assertEquals(LongStream.range(0, 10).sorted().distinct().toArray(), range);
+            assertEquals(LongStream.range(0, 10).parallel().sorted().distinct().toArray(), range);
+
+            long[] data = {5, 3, 1, 1, 5, 3, 9, 2, 9, 1, 0, 8};
+            long[] expected = {0, 1, 2, 3, 5, 8, 9};
+            assertEquals(LongStream.of(data).sorted().distinct().toArray(), expected);
+            assertEquals(LongStream.of(data).parallel().sorted().distinct().toArray(), expected);
+        }
+
+        {
+            long[] input = new Random().longs(100, -10, 10).map(x -> x+Long.MAX_VALUE).toArray();
+
+            TreeSet<Double> doubles = new TreeSet<>();
+            for(long i : input) doubles.add((double)i);
+            double[] expectedDoubles = doubles.stream().mapToDouble(Double::doubleValue).toArray();
+            assertEquals(LongStream.of(input).sorted().distinct().asDoubleStream()
+                         .sorted().distinct().toArray(), expectedDoubles);
+        }
+    }
+
     public void testSortSort() {
         Random r = new Random();