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 } } ``` 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]) + } }