From 2ffa4d72131ca5295d371f6fd0bdd4cf08009801 Mon Sep 17 00:00:00 2001 From: Stefan Zeiger Date: Fri, 1 Apr 2016 19:59:49 +0200 Subject: [PATCH 1/2] Automatic widening of numeric types for primitive Streams The widening conversions follow the usual pattern. Float becomes Double, Byte/Char/Short become Int. Boolean and Unit are not widened. For Arrays, where the actual data is unboxed, widening is implemented through custom Stepper implementations, so you can get e.g. a native IntStepper for an Array[Byte]. The StreamConverters hierarchy is modified to prefer these widened primitive steppers, so that, for example, the `seqStream` extension method on an Array[Byte] returns an IntStream. Other collection types with a boxed representation also get the same widening conversions (so you can still benefit from the better performance of unboxed streams) but there are no implicitly available widening steppers. Only the Stream extension methods fall back to applying a widening stepper in order to produce a widening primitive stream. Widening narrow numeric types for streams is consistent with the Streams API in the JDK. For example, `CharSequence.chars` returns an `IntStream` instead of a `Stream[Character]`. --- .../scala/compat/java8/StreamConverters.scala | 88 ++++++++++++++++++- .../compat/java8/collectionImpl/Stepper.scala | 45 +++++++++- .../java8/converterImpl/StepsArray.scala | 48 +++++----- .../compat/java8/StepConvertersTest.scala | 17 ++++ .../compat/java8/StreamConvertersTest.scala | 13 +++ 5 files changed, 181 insertions(+), 30 deletions(-) diff --git a/src/main/scala/scala/compat/java8/StreamConverters.scala b/src/main/scala/scala/compat/java8/StreamConverters.scala index 2c6affc..badb07b 100644 --- a/src/main/scala/scala/compat/java8/StreamConverters.scala +++ b/src/main/scala/scala/compat/java8/StreamConverters.scala @@ -14,7 +14,8 @@ trait PrimitiveStreamUnboxer[A, S] { def apply(boxed: Stream[A]): S } -trait Priority3StreamConverters { +trait Priority4StreamConverters { + // Fallback converters for AnySteppers that cannot be unboxed and widened to primitive streams implicit class EnrichAnySteppableWithParStream[A, CC](cc: CC)(implicit steppize: CC => MakesStepper[AnyStepper[A] with EfficientSubstep]) extends MakesParallelStream[A, Stream[A]] { def parStream: Stream[A] = StreamSupport.stream(steppize(cc).stepper.anticipateParallelism, true) @@ -25,7 +26,6 @@ trait Priority3StreamConverters { implicit class EnrichAnyValueSteppableWithParValueStream[V, CC](cc: CC)(implicit steppize: CC => MakesValueStepper[AnyStepper[V] with EfficientSubstep]) { def parValueStream: Stream[V] = StreamSupport.stream(steppize(cc).valueStepper.anticipateParallelism, true) } - // Note--conversion is only to make sure implicit conversion priority is lower than alternatives. implicit class EnrichScalaCollectionWithSeqStream[A, CC](cc: CC)(implicit steppize: CC => MakesStepper[AnyStepper[A]]) extends MakesSequentialStream[A, Stream[A]] { def seqStream: Stream[A] = StreamSupport.stream(steppize(cc).stepper, false) @@ -38,6 +38,90 @@ trait Priority3StreamConverters { } } +trait Priority3StreamConverters extends Priority4StreamConverters { + // Prefer to unbox and widen small primitive types over keeping them boxed + implicit class EnrichBoxedFloatSteppableWithParStream[CC](cc: CC)(implicit steppize: CC => MakesStepper[AnyStepper[Float] with EfficientSubstep]) + extends MakesParallelStream[java.lang.Double, DoubleStream] { + def parStream: DoubleStream = StreamSupport.doubleStream(new Stepper.WideningFloatStepper(steppize(cc).stepper.anticipateParallelism), true) + } + implicit class EnrichBoxedFloatKeySteppableWithParKeyStream[CC](cc: CC)(implicit steppize: CC => MakesKeyStepper[AnyStepper[Float] with EfficientSubstep]) { + def parKeyStream: DoubleStream = StreamSupport.doubleStream(new Stepper.WideningFloatStepper(steppize(cc).keyStepper.anticipateParallelism), true) + } + implicit class EnrichBoxedFloatValueSteppableWithParValueStream[CC](cc: CC)(implicit steppize: CC => MakesValueStepper[AnyStepper[Float] with EfficientSubstep]) { + def parValueStream: DoubleStream = StreamSupport.doubleStream(new Stepper.WideningFloatStepper(steppize(cc).valueStepper.anticipateParallelism), true) + } + implicit class EnrichBoxedByteSteppableWithParStream[CC](cc: CC)(implicit steppize: CC => MakesStepper[AnyStepper[Byte] with EfficientSubstep]) + extends MakesParallelStream[java.lang.Integer, IntStream] { + def parStream: IntStream = StreamSupport.intStream(new Stepper.WideningByteStepper(steppize(cc).stepper.anticipateParallelism), true) + } + implicit class EnrichBoxedByteKeySteppableWithParKeyStream[CC](cc: CC)(implicit steppize: CC => MakesKeyStepper[AnyStepper[Byte] with EfficientSubstep]) { + def parKeyStream: IntStream = StreamSupport.intStream(new Stepper.WideningByteStepper(steppize(cc).keyStepper.anticipateParallelism), true) + } + implicit class EnrichBoxedByteValueSteppableWithParValueStream[CC](cc: CC)(implicit steppize: CC => MakesValueStepper[AnyStepper[Byte] with EfficientSubstep]) { + def parValueStream: IntStream = StreamSupport.intStream(new Stepper.WideningByteStepper(steppize(cc).valueStepper.anticipateParallelism), true) + } + implicit class EnrichBoxedShortSteppableWithParStream[CC](cc: CC)(implicit steppize: CC => MakesStepper[AnyStepper[Short] with EfficientSubstep]) + extends MakesParallelStream[java.lang.Integer, IntStream] { + def parStream: IntStream = StreamSupport.intStream(new Stepper.WideningShortStepper(steppize(cc).stepper.anticipateParallelism), true) + } + implicit class EnrichBoxedShortKeySteppableWithParKeyStream[CC](cc: CC)(implicit steppize: CC => MakesKeyStepper[AnyStepper[Short] with EfficientSubstep]) { + def parKeyStream: IntStream = StreamSupport.intStream(new Stepper.WideningShortStepper(steppize(cc).keyStepper.anticipateParallelism), true) + } + implicit class EnrichBoxedShortValueSteppableWithParValueStream[CC](cc: CC)(implicit steppize: CC => MakesValueStepper[AnyStepper[Short] with EfficientSubstep]) { + def parValueStream: IntStream = StreamSupport.intStream(new Stepper.WideningShortStepper(steppize(cc).valueStepper.anticipateParallelism), true) + } + implicit class EnrichBoxedCharSteppableWithParStream[CC](cc: CC)(implicit steppize: CC => MakesStepper[AnyStepper[Char] with EfficientSubstep]) + extends MakesParallelStream[java.lang.Integer, IntStream] { + def parStream: IntStream = StreamSupport.intStream(new Stepper.WideningCharStepper(steppize(cc).stepper.anticipateParallelism), true) + } + implicit class EnrichBoxedCharKeySteppableWithParKeyStream[CC](cc: CC)(implicit steppize: CC => MakesKeyStepper[AnyStepper[Char] with EfficientSubstep]) { + def parKeyStream: IntStream = StreamSupport.intStream(new Stepper.WideningCharStepper(steppize(cc).keyStepper.anticipateParallelism), true) + } + implicit class EnrichBoxedCharValueSteppableWithParValueStream[CC](cc: CC)(implicit steppize: CC => MakesValueStepper[AnyStepper[Char] with EfficientSubstep]) { + def parValueStream: IntStream = StreamSupport.intStream(new Stepper.WideningCharStepper(steppize(cc).valueStepper.anticipateParallelism), true) + } + implicit class EnrichBoxedFloatSteppableWithSeqStream[CC](cc: CC)(implicit steppize: CC => MakesStepper[AnyStepper[Float]]) + extends MakesSequentialStream[java.lang.Double, DoubleStream] { + def seqStream: DoubleStream = StreamSupport.doubleStream(new Stepper.WideningFloatStepper(steppize(cc).stepper), false) + } + implicit class EnrichBoxedFloatKeySteppableWithSeqKeyStream[CC](cc: CC)(implicit steppize: CC => MakesKeyStepper[AnyStepper[Float]]) { + def seqKeyStream: DoubleStream = StreamSupport.doubleStream(new Stepper.WideningFloatStepper(steppize(cc).keyStepper), false) + } + implicit class EnrichBoxedFloatValueSteppableWithSeqValueStream[CC](cc: CC)(implicit steppize: CC => MakesValueStepper[AnyStepper[Float]]) { + def seqValueStream: DoubleStream = StreamSupport.doubleStream(new Stepper.WideningFloatStepper(steppize(cc).valueStepper), false) + } + implicit class EnrichBoxedByteSteppableWithSeqStream[CC](cc: CC)(implicit steppize: CC => MakesStepper[AnyStepper[Byte]]) + extends MakesSequentialStream[java.lang.Integer, IntStream] { + def seqStream: IntStream = StreamSupport.intStream(new Stepper.WideningByteStepper(steppize(cc).stepper), false) + } + implicit class EnrichBoxedByteKeySteppableWithSeqKeyStream[CC](cc: CC)(implicit steppize: CC => MakesKeyStepper[AnyStepper[Byte]]) { + def seqKeyStream: IntStream = StreamSupport.intStream(new Stepper.WideningByteStepper(steppize(cc).keyStepper), false) + } + implicit class EnrichBoxedByteValueSteppableWithSeqValueStream[CC](cc: CC)(implicit steppize: CC => MakesValueStepper[AnyStepper[Byte]]) { + def seqValueStream: IntStream = StreamSupport.intStream(new Stepper.WideningByteStepper(steppize(cc).valueStepper), false) + } + implicit class EnrichBoxedShortSteppableWithSeqStream[CC](cc: CC)(implicit steppize: CC => MakesStepper[AnyStepper[Short]]) + extends MakesSequentialStream[java.lang.Integer, IntStream] { + def seqStream: IntStream = StreamSupport.intStream(new Stepper.WideningShortStepper(steppize(cc).stepper), false) + } + implicit class EnrichBoxedShortKeySteppableWithSeqKeyStream[CC](cc: CC)(implicit steppize: CC => MakesKeyStepper[AnyStepper[Short]]) { + def seqKeyStream: IntStream = StreamSupport.intStream(new Stepper.WideningShortStepper(steppize(cc).keyStepper), false) + } + implicit class EnrichBoxedShortValueSteppableWithSeqValueStream[CC](cc: CC)(implicit steppize: CC => MakesValueStepper[AnyStepper[Short]]) { + def seqValueStream: IntStream = StreamSupport.intStream(new Stepper.WideningShortStepper(steppize(cc).valueStepper), false) + } + implicit class EnrichBoxedCharSteppableWithSeqStream[CC](cc: CC)(implicit steppize: CC => MakesStepper[AnyStepper[Char]]) + extends MakesSequentialStream[java.lang.Integer, IntStream] { + def seqStream: IntStream = StreamSupport.intStream(new Stepper.WideningCharStepper(steppize(cc).stepper), false) + } + implicit class EnrichBoxedCharKeySteppableWithSeqKeyStream[CC](cc: CC)(implicit steppize: CC => MakesKeyStepper[AnyStepper[Char]]) { + def seqKeyStream: IntStream = StreamSupport.intStream(new Stepper.WideningCharStepper(steppize(cc).keyStepper), false) + } + implicit class EnrichBoxedCharValueSteppableWithSeqValueStream[CC](cc: CC)(implicit steppize: CC => MakesValueStepper[AnyStepper[Char]]) { + def seqValueStream: IntStream = StreamSupport.intStream(new Stepper.WideningCharStepper(steppize(cc).valueStepper), false) + } +} + trait Priority2StreamConverters extends Priority3StreamConverters { implicit class EnrichDoubleSteppableWithParStream[CC](cc: CC)(implicit steppize: CC => MakesStepper[DoubleStepper with EfficientSubstep]) extends MakesParallelStream[java.lang.Double, DoubleStream] { diff --git a/src/main/scala/scala/compat/java8/collectionImpl/Stepper.scala b/src/main/scala/scala/compat/java8/collectionImpl/Stepper.scala index c40b7bd..e315024 100644 --- a/src/main/scala/scala/compat/java8/collectionImpl/Stepper.scala +++ b/src/main/scala/scala/compat/java8/collectionImpl/Stepper.scala @@ -239,8 +239,8 @@ trait AnyStepper[A] extends Stepper[A] with java.util.Iterator[A] with Spliterat def parStream: java.util.stream.Stream[A] = java.util.stream.StreamSupport.stream(this, true) } -object AnyStepper { - private[collectionImpl] class BoxedDoubleStepper(st: DoubleStepper) extends AnyStepper[Double] { +private[collectionImpl] object AnyStepper { + final class BoxedDoubleStepper(st: DoubleStepper) extends AnyStepper[Double] { def hasNext(): Boolean = st.hasNext() def next(): Double = st.next() def characteristics(): Int = st.characteristics() @@ -248,7 +248,7 @@ object AnyStepper { def substep(): AnyStepper[Double] = new BoxedDoubleStepper(st.substep()) } - private[collectionImpl] class BoxedIntStepper(st: IntStepper) extends AnyStepper[Int] { + final class BoxedIntStepper(st: IntStepper) extends AnyStepper[Int] { def hasNext(): Boolean = st.hasNext() def next(): Int = st.next() def characteristics(): Int = st.characteristics() @@ -256,7 +256,7 @@ object AnyStepper { def substep(): AnyStepper[Int] = new BoxedIntStepper(st.substep()) } - private[collectionImpl] class BoxedLongStepper(st: LongStepper) extends AnyStepper[Long] { + final class BoxedLongStepper(st: LongStepper) extends AnyStepper[Long] { def hasNext(): Boolean = st.hasNext() def next(): Long = st.next() def characteristics(): Int = st.characteristics() @@ -564,4 +564,41 @@ object Stepper { case _ => new OfLongSpliterator(sp) } + /* These adapter classes can wrap an AnyStepper of a small numeric type into the appropriately widened + * primitive Stepper type. This provides a basis for more efficient stream processing on unboxed values + * provided that the original source of the data is already boxed. In other cases the widening conversion + * should always be performed directly on the original unboxed values in a custom Stepper implementation + * (see for example StepsWidenedByteArray). */ + + private[java8] class WideningByteStepper(st: AnyStepper[Byte]) extends IntStepper { + def hasNext(): Boolean = st.hasNext() + def nextInt(): Int = st.next() + def characteristics(): Int = st.characteristics() | NonNull + def estimateSize(): Long = st.estimateSize() + def substep(): IntStepper = new WideningByteStepper(st.substep()) + } + + private[java8] class WideningCharStepper(st: AnyStepper[Char]) extends IntStepper { + def hasNext(): Boolean = st.hasNext() + def nextInt(): Int = st.next() + def characteristics(): Int = st.characteristics() | NonNull + def estimateSize(): Long = st.estimateSize() + def substep(): IntStepper = new WideningCharStepper(st.substep()) + } + + private[java8] class WideningShortStepper(st: AnyStepper[Short]) extends IntStepper { + def hasNext(): Boolean = st.hasNext() + def nextInt(): Int = st.next() + def characteristics(): Int = st.characteristics() | NonNull + def estimateSize(): Long = st.estimateSize() + def substep(): IntStepper = new WideningShortStepper(st.substep()) + } + + private[java8] class WideningFloatStepper(st: AnyStepper[Float]) extends DoubleStepper { + def hasNext(): Boolean = st.hasNext() + def nextDouble(): Double = st.next() + def characteristics(): Int = st.characteristics() | NonNull + def estimateSize(): Long = st.estimateSize() + def substep(): DoubleStepper = new WideningFloatStepper(st.substep()) + } } diff --git a/src/main/scala/scala/compat/java8/converterImpl/StepsArray.scala b/src/main/scala/scala/compat/java8/converterImpl/StepsArray.scala index e4f6f01..8a960d0 100644 --- a/src/main/scala/scala/compat/java8/converterImpl/StepsArray.scala +++ b/src/main/scala/scala/compat/java8/converterImpl/StepsArray.scala @@ -35,28 +35,28 @@ extends StepsLikeIndexed[Boolean, StepsBoxedBooleanArray](_i0, _iN) { def semiclone(half: Int) = new StepsBoxedBooleanArray(underlying, i0, half) } -private[java8] class StepsBoxedByteArray(underlying: Array[Byte], _i0: Int, _iN: Int) -extends StepsLikeIndexed[Byte, StepsBoxedByteArray](_i0, _iN) { - def next() = if (hasNext()) { val j = i0; i0 += 1; underlying(j) } else throwNSEE - def semiclone(half: Int) = new StepsBoxedByteArray(underlying, i0, half) +private[java8] class StepsWidenedByteArray(underlying: Array[Byte], _i0: Int, _iN: Int) +extends StepsIntLikeIndexed[StepsWidenedByteArray](_i0, _iN) { + def nextInt() = if (hasNext()) { val j = i0; i0 += 1; underlying(j) } else throwNSEE + def semiclone(half: Int) = new StepsWidenedByteArray(underlying, i0, half) } -private[java8] class StepsBoxedCharArray(underlying: Array[Char], _i0: Int, _iN: Int) -extends StepsLikeIndexed[Char, StepsBoxedCharArray](_i0, _iN) { - def next() = if (hasNext()) { val j = i0; i0 += 1; underlying(j) } else throwNSEE - def semiclone(half: Int) = new StepsBoxedCharArray(underlying, i0, half) +private[java8] class StepsWidenedCharArray(underlying: Array[Char], _i0: Int, _iN: Int) +extends StepsIntLikeIndexed[StepsWidenedCharArray](_i0, _iN) { + def nextInt() = if (hasNext()) { val j = i0; i0 += 1; underlying(j) } else throwNSEE + def semiclone(half: Int) = new StepsWidenedCharArray(underlying, i0, half) } -private[java8] class StepsBoxedShortArray(underlying: Array[Short], _i0: Int, _iN: Int) -extends StepsLikeIndexed[Short, StepsBoxedShortArray](_i0, _iN) { - def next() = if (hasNext()) { val j = i0; i0 += 1; underlying(j) } else throwNSEE - def semiclone(half: Int) = new StepsBoxedShortArray(underlying, i0, half) +private[java8] class StepsWidenedShortArray(underlying: Array[Short], _i0: Int, _iN: Int) +extends StepsIntLikeIndexed[StepsWidenedShortArray](_i0, _iN) { + def nextInt() = if (hasNext()) { val j = i0; i0 += 1; underlying(j) } else throwNSEE + def semiclone(half: Int) = new StepsWidenedShortArray(underlying, i0, half) } -private[java8] class StepsBoxedFloatArray(underlying: Array[Float], _i0: Int, _iN: Int) -extends StepsLikeIndexed[Float, StepsBoxedFloatArray](_i0, _iN) { - def next() = if (hasNext()) { val j = i0; i0 += 1; underlying(j) } else throwNSEE - def semiclone(half: Int) = new StepsBoxedFloatArray(underlying, i0, half) +private[java8] class StepsWidenedFloatArray(underlying: Array[Float], _i0: Int, _iN: Int) +extends StepsDoubleLikeIndexed[StepsWidenedFloatArray](_i0, _iN) { + def nextDouble() = if (hasNext()) { val j = i0; i0 += 1; underlying(j) } else throwNSEE + def semiclone(half: Int) = new StepsWidenedFloatArray(underlying, i0, half) } private[java8] class StepsDoubleArray(underlying: Array[Double], _i0: Int, _iN: Int) @@ -97,20 +97,20 @@ final class RichArrayBooleanCanStep(private val underlying: Array[Boolean]) exte @inline def stepper: AnyStepper[Boolean] with EfficientSubstep = new StepsBoxedBooleanArray(underlying, 0, underlying.length) } -final class RichArrayByteCanStep(private val underlying: Array[Byte]) extends AnyVal with MakesStepper[AnyStepper[Byte] with EfficientSubstep] { - @inline def stepper: AnyStepper[Byte] with EfficientSubstep = new StepsBoxedByteArray(underlying, 0, underlying.length) +final class RichArrayByteCanStep(private val underlying: Array[Byte]) extends AnyVal with MakesStepper[IntStepper with EfficientSubstep] { + @inline def stepper: IntStepper with EfficientSubstep = new StepsWidenedByteArray(underlying, 0, underlying.length) } -final class RichArrayCharCanStep(private val underlying: Array[Char]) extends AnyVal with MakesStepper[AnyStepper[Char] with EfficientSubstep] { - @inline def stepper: AnyStepper[Char] with EfficientSubstep = new StepsBoxedCharArray(underlying, 0, underlying.length) +final class RichArrayCharCanStep(private val underlying: Array[Char]) extends AnyVal with MakesStepper[IntStepper with EfficientSubstep] { + @inline def stepper: IntStepper with EfficientSubstep = new StepsWidenedCharArray(underlying, 0, underlying.length) } -final class RichArrayShortCanStep(private val underlying: Array[Short]) extends AnyVal with MakesStepper[AnyStepper[Short] with EfficientSubstep] { - @inline def stepper: AnyStepper[Short] with EfficientSubstep = new StepsBoxedShortArray(underlying, 0, underlying.length) +final class RichArrayShortCanStep(private val underlying: Array[Short]) extends AnyVal with MakesStepper[IntStepper with EfficientSubstep] { + @inline def stepper: IntStepper with EfficientSubstep = new StepsWidenedShortArray(underlying, 0, underlying.length) } -final class RichArrayFloatCanStep(private val underlying: Array[Float]) extends AnyVal with MakesStepper[AnyStepper[Float] with EfficientSubstep] { - @inline def stepper: AnyStepper[Float] with EfficientSubstep = new StepsBoxedFloatArray(underlying, 0, underlying.length) +final class RichArrayFloatCanStep(private val underlying: Array[Float]) extends AnyVal with MakesStepper[DoubleStepper with EfficientSubstep] { + @inline def stepper: DoubleStepper with EfficientSubstep = new StepsWidenedFloatArray(underlying, 0, underlying.length) } final class RichArrayDoubleCanStep(private val underlying: Array[Double]) extends AnyVal with MakesStepper[DoubleStepper with EfficientSubstep] { diff --git a/src/test/scala/scala/compat/java8/StepConvertersTest.scala b/src/test/scala/scala/compat/java8/StepConvertersTest.scala index 2f09927..c4597d5 100644 --- a/src/test/scala/scala/compat/java8/StepConvertersTest.scala +++ b/src/test/scala/scala/compat/java8/StepConvertersTest.scala @@ -437,6 +437,23 @@ class StepConvertersTest { Okay( (cc.TrieMap[Int, Int](0xDEEDED -> 654321): cc.Map[Int, Int]).valueStepper ) } + @Test + def shortWidening() { + implicit val spec = SpecCheck(_.isInstanceOf[IntStepper]) + + good( Array[Short](654321.toShort).stepper ) + + //TODO: None of these currently work because there are no native Stepper implementations. This does not only + // affect widening conversions though. While you can get, for example, an IntStepper for a WrappedArray[Int], + // all values have to go through a boxing/unboxing step! + + //good( ci.NumericRange(123456.toShort, 123458.toShort, 1.toShort).stepper ) + //good( ((Array[Short](654321.toShort): cm.WrappedArray[Short]): cm.ArrayLike[Short, cm.WrappedArray[Short]]).stepper ) + //good( (Array[Short](654321.toShort): cm.ArrayOps[Short]).stepper ) + //good( cm.ResizableArray[Short](654321.toShort).stepper ) + //good( (Array[Short](654321.toShort): cm.WrappedArray[Short]).stepper ) + } + @Test def comprehensivelyLong() { implicit val spec = SpecCheck(_.isInstanceOf[LongStepper]) diff --git a/src/test/scala/scala/compat/java8/StreamConvertersTest.scala b/src/test/scala/scala/compat/java8/StreamConvertersTest.scala index b1ad2f5..7ce1340 100644 --- a/src/test/scala/scala/compat/java8/StreamConvertersTest.scala +++ b/src/test/scala/scala/compat/java8/StreamConvertersTest.scala @@ -234,4 +234,17 @@ class StreamConvertersTest { assert(hsL.parStream.isInstanceOf[LongStream]) } } + + @Test + def primitiveStreamTypes(): Unit = { + // Unboxed native + widening Steppers available: + assertEquals(Vector[Int](1, 2, 3), (Array[Int](1, 2, 3).seqStream: IntStream).toScala[Vector]) + assertEquals(Vector[Short](1.toShort, 2.toShort, 3.toShort), (Array[Short](1.toShort, 2.toShort, 3.toShort).seqStream: IntStream).toScala[Vector]) + assertEquals(Vector[String]("a", "b"), (Array[String]("a", "b").seqStream: Stream[String]).toScala[Vector]) + + // Boxed collections, widening via boxed AnySteppers: + assertEquals(Vector[Int](1, 2, 3), (Vector[Int](1, 2, 3).seqStream: IntStream).toScala[Vector]) + assertEquals(Vector[Short](1.toShort, 2.toShort, 3.toShort), (Vector[Short](1.toShort, 2.toShort, 3.toShort).seqStream: IntStream).toScala[Vector]) + assertEquals(Vector[String]("a", "b"), (Vector[String]("a", "b").seqStream: Stream[String]).toScala[Vector]) + } } From f029ed563d38338e4b9a3990f4025e23bb338738 Mon Sep 17 00:00:00 2001 From: Stefan Zeiger Date: Wed, 6 Apr 2016 15:24:16 +0200 Subject: [PATCH 2/2] Improve README Explaining widening conversions, fixing errors, and clarifying the role of Steppers in the API. --- README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 54fe1b1..e6caf3f 100644 --- a/README.md +++ b/README.md @@ -121,22 +121,27 @@ class Test { Scala collections gain `seqStream` and `parStream` as extension methods that produce a Java 8 Stream running sequentially or in parallel, respectively. These are automatically specialized to a primitive -type if possible. For instance, `List(1,2).seqStream` produces an `IntStream`. Maps additionally have +type if possible, including automatically applied widening conversions. For instance, `List(1,2).seqStream` +produces an `IntStream`, and so does `List(1.toShort, 2.toShort).parStream`. Maps additionally have `seqKeyStream`, `seqValueStream`, `parKeyStream`, and `parValueStream` methods. Scala collections also gain `accumulate` and `stepper` methods that produce utility collections that can be useful when working with Java 8 Streams. `accumulate` produces an `Accumulator` or its primitive counterpart (`DoubleAccumulator`, etc.), which is a low-level collection designed for efficient collection and dispatching of results to and from Streams. Unlike most collections, it can contain more than -`Int.MaxValue` elements. `stepper` produces a `Stepper` which is a fusion of `Spliterator` and `Iterator`. -`Stepper`s underlie the Scala collections' instances of Java 8 Streams. +`Int.MaxValue` elements. + +`stepper` produces a `Stepper` which is a fusion of `Spliterator` and `Iterator`. `Stepper`s underlie the Scala +collections' instances of Java 8 Streams. Steppers are intended as low-level building blocks for streams. +Usually you would not create them directly or call their methods but you can implement them alongside custom +collections to get better performance when streaming from these collections. Java 8 Streams gain `toScala[Coll]` and `accumulate` methods, to make it easy to produce Scala collections or Accumulators, respectively, from Java 8 Streams. For instance, `myStream.to[Vector]` will collect the contents of a Stream into a `scala.collection.immutable.Vector`. Note that standard sequential builders are used for collections, so this is best done to gather the results of an expensive computation. -Finally, there is a Java class, `ScalaStreamer`, that has a series of `from` methods that can be used to +Finally, there is a Java class, `ScalaStreamSupport`, that has a series of `stream` methods that can be used to obtain Java 8 Streams from Scala collections from within Java. #### Performance Considerations @@ -218,7 +223,7 @@ def mapToSortedString[A](xs: Vector[A], f: A => String, sep: String) = #### Java Usage Example To convert a Scala collection to a Java 8 Stream from within Java, it usually -suffices to call `ScalaStreaming.from(xs)` on your collection `xs`. If `xs` is +suffices to call `ScalaStreamSupport.stream(xs)` on your collection `xs`. If `xs` is a map, you may wish to get the keys or values alone by using `fromKeys` or `fromValues`. If the collection has an underlying representation that is not efficiently parallelized (e.g. `scala.collection.immutable.List`), then @@ -237,14 +242,14 @@ Here is an example of conversion of a Scala collection within Java 8: ```java import scala.collection.mutable.ArrayBuffer; -import scala.compat.java8.ScalaStreaming; +import scala.compat.java8.ScalaStreamSupport; public class StreamConvertersExample { public int MakeAndUseArrayBuffer() { ArrayBuffer ab = new ArrayBuffer(); ab.$plus$eq("salmon"); ab.$plus$eq("herring"); - return ScalaStreaming.from(ab).mapToInt(x -> x.length()).sum(); // 6+7 = 13 + return ScalaStreamSupport.stream(ab).mapToInt(x -> x.length()).sum(); // 6+7 = 13 } } ```