Skip to content

Commit

Permalink
[#269] Preliminary mapMulti implementation (no Java16+ version-specif…
Browse files Browse the repository at this point in the history
…ic code yet)
  • Loading branch information
amaembo committed Nov 4, 2023
1 parent 9ff22d4 commit c1346ce
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 15 deletions.
89 changes: 78 additions & 11 deletions src/main/java/one/util/streamex/AbstractStreamEx.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,7 @@
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.ForkJoinPool;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -205,6 +195,83 @@ public <R> StreamEx<R> flatMap(Function<? super T, ? extends Stream<? extends R>
return new StreamEx<>(stream().flatMap(mapper), context);
}

/**
* Returns a stream where every element of this stream is replaced by elements produced
* by a mapper function.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* function that generates replacement elements. It accepts an element of this
* stream and a consumer that accepts new elements produced from the input element.
* @return the new stream
* @param <R> type of the resulting elements
* @since 0.8.3
*/
public <R> StreamEx<R> mapMulti(BiConsumer<? super T, ? super Consumer<R>> mapper) {
Objects.requireNonNull(mapper);
return VerSpec.VER_SPEC.callMapMulti(this, mapper);
}

/**
* Returns an {@link IntStreamEx} where every element of this stream is replaced by elements produced
* by a mapper function.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* function that generates replacement elements. It accepts an element of this
* stream and a consumer that accepts new elements produced from the input element.
* @return the new stream
* @since 0.8.3
*/
public IntStreamEx mapMultiToInt(BiConsumer<? super T, ? super IntConsumer> mapper) {
Objects.requireNonNull(mapper);
return VerSpec.VER_SPEC.callMapMultiToInt(this, mapper);
}

/**
* Returns a {@link LongStreamEx} where every element of this stream is replaced by elements produced
* by a mapper function.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* function that generates replacement elements. It accepts an element of this
* stream and a consumer that accepts new elements produced from the input element.
* @return the new stream
* @since 0.8.3
*/
public LongStreamEx mapMultiToLong(BiConsumer<? super T, ? super LongConsumer> mapper) {
Objects.requireNonNull(mapper);
return VerSpec.VER_SPEC.callMapMultiToLong(this, mapper);
}

/**
* Returns a {@link DoubleStreamEx} where every element of this stream is replaced by elements produced
* by a mapper function.
*
* <p>This is an <a href="package-summary.html#StreamOps">intermediate
* operation</a>.
*
* @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* function that generates replacement elements. It accepts an element of this
* stream and a consumer that accepts new elements produced from the input element.
* @return the new stream
* @since 0.8.3
*/
public DoubleStreamEx mapMultiToDouble(BiConsumer<? super T, ? super DoubleConsumer> mapper) {
Objects.requireNonNull(mapper);
return VerSpec.VER_SPEC.callMapMultiToDouble(this, mapper);
}

@Override
public <R> StreamEx<R> map(Function<? super T, ? extends R> mapper) {
return new StreamEx<>(stream().map(mapper), context);
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/one/util/streamex/Internals.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ void addAll(IntBuffer buf) {
System.arraycopy(buf.data, 0, data, size, buf.size);
size += buf.size;
}

IntStreamEx stream() {
return IntStreamEx.of(data, 0, size);
}

int[] toArray() {
return data.length == size ? data : Arrays.copyOfRange(data, 0, size);
Expand Down Expand Up @@ -277,6 +281,10 @@ void addAll(LongBuffer buf) {
size += buf.size;
}

LongStreamEx stream() {
return LongStreamEx.of(data, 0, size);
}

long[] toArray() {
return data.length == size ? data : Arrays.copyOfRange(data, 0, size);
}
Expand Down Expand Up @@ -309,6 +317,10 @@ void addAll(DoubleBuffer buf) {
size += buf.size;
}

DoubleStreamEx stream() {
return DoubleStreamEx.of(data, 0, size);
}

double[] toArray() {
return data.length == size ? data : Arrays.copyOfRange(data, 0, size);
}
Expand Down
39 changes: 35 additions & 4 deletions src/main/java/one/util/streamex/VersionSpecific.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
package one.util.streamex;

import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Spliterator;
import java.util.function.DoublePredicate;
import java.util.function.IntPredicate;
import java.util.function.LongPredicate;
import java.util.function.Predicate;
import java.util.function.*;
import java.util.stream.IntStream;

/**
Expand All @@ -47,6 +46,38 @@ DoubleStreamEx callWhile(DoubleStreamEx stream, DoublePredicate predicate, boole
return stream.delegate(new TakeDrop.TDOfDouble(stream.spliterator(), drop, false, predicate));
}

<T, R> StreamEx<R> callMapMulti(AbstractStreamEx<T, ?> s, BiConsumer<? super T, ? super Consumer<R>> mapper) {
return s.flatCollection(e -> {
List<R> result = new ArrayList<>();
mapper.accept(e, (Consumer<R>) result::add);
return result;
});
}

<T> IntStreamEx callMapMultiToInt(AbstractStreamEx<T, ?> s, BiConsumer<? super T, ? super IntConsumer> mapper) {
return s.flatMapToInt(e -> {
Internals.IntBuffer result = new Internals.IntBuffer();
mapper.accept(e, (IntConsumer) result::add);
return result.stream();
});
}

<T> LongStreamEx callMapMultiToLong(AbstractStreamEx<T, ?> s, BiConsumer<? super T, ? super LongConsumer> mapper) {
return s.flatMapToLong(e -> {
Internals.LongBuffer result = new Internals.LongBuffer();
mapper.accept(e, (LongConsumer) result::add);
return result.stream();
});
}

<T> DoubleStreamEx callMapMultiToDouble(AbstractStreamEx<T, ?> s, BiConsumer<? super T, ? super DoubleConsumer> mapper) {
return s.flatMapToDouble(e -> {
Internals.DoubleBuffer result = new Internals.DoubleBuffer();
mapper.accept(e, (DoubleConsumer) result::add);
return result.stream();
});
}

IntStream ofChars(CharSequence seq) {
// In JDK 8 there's only default chars() method which uses
// IteratorSpliterator
Expand Down
34 changes: 34 additions & 0 deletions src/test/java/one/util/streamex/api/StreamExApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import one.util.streamex.StreamEx;

import static java.util.Arrays.asList;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

/**
Expand All @@ -48,4 +49,37 @@ public void testPrepend() {
List<String> expected = asList("c", "b", "a");
assertEquals(expected, input.stream().map(StreamEx::of).reduce(StreamEx::prepend).get().toList());
}

@Test
public void testMapMulti() {
List<String> result = StreamEx.of("abc", "def", "gh")
.<String>mapMulti((s, cons) -> s.chars()
.mapToObj(ch -> ""+(char)ch).forEach(cons))
.toList();
assertEquals(asList("a", "b", "c", "d", "e", "f", "g", "h"), result);
}

@Test
public void testMapMultiToInt() {
int[] result = StreamEx.of("abc", "def", "gh")
.mapMultiToInt((s, cons) -> s.chars().forEach(cons))
.toArray();
assertArrayEquals(new int[] {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}, result);
}

@Test
public void testMapMultiToLong() {
long[] result = StreamEx.of("abc", "def", "gh")
.mapMultiToLong((s, cons) -> s.chars().asLongStream().forEach(cons))
.toArray();
assertArrayEquals(new long[] {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}, result);
}

@Test
public void testMapMultiToDouble() {
double[] result = StreamEx.of("abc", "def", "gh")
.mapMultiToDouble((s, cons) -> s.chars().asDoubleStream().forEach(cons))
.toArray();
assertArrayEquals(new double[] {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}, result, 0.0);
}
}

0 comments on commit c1346ce

Please sign in to comment.