Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Traversable<T>.tapEach(Consumer<? super T>) #2681

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/AbstractMultimap.java
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,11 @@ public M peek(Consumer<? super Tuple2<K, V>> action) {
return (M) this;
}

@Override
public M tapEach(Consumer<? super Tuple2<K, V>> action) {
return peek(action);
}

@SuppressWarnings("unchecked")
@Override
public M replace(Tuple2<K, V> currentElement, Tuple2<K, V> newElement) {
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/io/vavr/collection/Array.java
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,16 @@ public Array<T> peek(Consumer<? super T> action) {
return this;
}

@Override
public Array<T> tapEach(Consumer<? super T> action) {
Objects.requireNonNull(action, "action is null");
for (int i = 0; i < length(); i++) {
final T value = get(i);
action.accept(value);
}
return this;
}

@Override
public Array<Array<T>> permutations() {
if (isEmpty()) {
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/BitSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,11 @@ public final BitSet<T> peek(Consumer<? super T> action) {
return this;
}

@Override
public final BitSet<T> tapEach(Consumer<? super T> action) {
return Collections.tapEach(this, action);
}

@Override
public final String stringPrefix() {
return "BitSet";
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/io/vavr/collection/CharSeq.java
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,15 @@ public CharSeq peek(Consumer<? super Character> action) {
return this;
}

@Override
public CharSeq tapEach(Consumer<? super Character> action) {
Objects.requireNonNull(action, "action is null");
for (int i = 0; i < back.length(); i++) {
action.accept(get(i));
}
return this;
}

@Override
public IndexedSeq<CharSeq> permutations() {
if (isEmpty()) {
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/io/vavr/collection/Collections.java
Original file line number Diff line number Diff line change
Expand Up @@ -589,4 +589,12 @@ Object[] toArray() {
}
}

@SuppressWarnings("unchecked")
static <T, C extends Traversable<T>> C tapEach(C source, Consumer<? super T> action) {
Objects.requireNonNull(action, "action must not be null");
return (C) source.map(t -> {
action.accept(t);
return t;
});
}
}
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/HashMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,11 @@ public HashMap<K, V> peek(Consumer<? super Tuple2<K, V>> action) {
return Maps.peek(this, action);
}

@Override
public HashMap<K, V> tapEach(Consumer<? super Tuple2<K, V>> action) {
return Collections.tapEach(this, action);
}

@Override
public <U extends V> HashMap<K, V> put(K key, U value, BiFunction<? super V, ? super U, ? extends V> merge) {
return Maps.put(this, key, value, merge);
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/HashSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,11 @@ public HashSet<T> peek(Consumer<? super T> action) {
return this;
}

@Override
public HashSet<T> tapEach(Consumer<? super T> action) {
return Collections.tapEach(this, action);
}

@Override
public HashSet<T> remove(T element) {
final HashArrayMappedTrie<T, T> newTree = tree.remove(element);
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/Iterator.java
Original file line number Diff line number Diff line change
Expand Up @@ -1747,6 +1747,11 @@ public T next() {
}
}

@Override
default Iterator<T> tapEach(Consumer<? super T> action) {
return peek(action);
}

@Override
default T reduceLeft(BiFunction<? super T, ? super T, ? extends T> op) {
Objects.requireNonNull(op, "op is null");
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/io/vavr/collection/LinearSeq.java
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ default int lastIndexWhere(Predicate<? super T> predicate, int end) {
@Override
LinearSeq<T> peek(Consumer<? super T> action);

@Override
LinearSeq<T> tapEach(Consumer<? super T> action);

@Override
LinearSeq<? extends LinearSeq<T>> permutations();

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/LinkedHashMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,11 @@ public LinkedHashMap<K, V> peek(Consumer<? super Tuple2<K, V>> action) {
return Maps.peek(this, action);
}

@Override
public LinkedHashMap<K, V> tapEach(Consumer<? super Tuple2<K, V>> action) {
return Collections.tapEach(this, action);
}

@Override
public <U extends V> LinkedHashMap<K, V> put(K key, U value, BiFunction<? super V, ? super U, ? extends V> merge) {
return Maps.put(this, key, value, merge);
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/LinkedHashSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,11 @@ public LinkedHashSet<T> peek(Consumer<? super T> action) {
return this;
}

@Override
public LinkedHashSet<T> tapEach(Consumer<? super T> action) {
return Collections.tapEach(this, action);
}

@Override
public LinkedHashSet<T> remove(T element) {
final LinkedHashMap<T, Object> newMap = map.remove(element);
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/List.java
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,11 @@ public final List<T> peek(Consumer<? super T> action) {
return this;
}

@Override
public List<T> tapEach(Consumer<? super T> action) {
return Collections.tapEach(this, action);
}

@Override
public final List<List<T>> permutations() {
if (isEmpty()) {
Expand Down
9 changes: 5 additions & 4 deletions src/main/java/io/vavr/collection/PriorityQueue.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@
import java.util.Comparator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.*;
import java.util.stream.Collector;

/**
Expand Down Expand Up @@ -663,6 +660,10 @@ public Comparator<T> comparator() {
return (Comparator<T>) comparator;
}

@Override
public PriorityQueue<T> tapEach(Consumer<? super T> action) {
return Collections.tapEach(this, action);
}

/**
* fun deleteMin [] = raise EMPTY
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/Queue.java
Original file line number Diff line number Diff line change
Expand Up @@ -1277,6 +1277,11 @@ public Queue<T> takeRightWhile(Predicate<? super T> predicate) {
return takeRightUntil(predicate.negate());
}

@Override
public Queue<T> tapEach(Consumer<? super T> action) {
return Collections.tapEach(this, action);
}

/**
* Transforms this {@code Queue}.
*
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/io/vavr/collection/Seq.java
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,9 @@ default <U> U foldRight(U zero, BiFunction<? super T, ? super U, ? extends U> f)
@Override
Seq<T> peek(Consumer<? super T> action);

@Override
Seq<T> tapEach(Consumer<? super T> action);

@Override
Seq<T> replace(T currentElement, T newElement);

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/io/vavr/collection/Set.java
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ default boolean isDistinct() {
@Override
Set<T> peek(Consumer<? super T> action);

@Override
Set<T> tapEach(Consumer<? super T> action);

@Override
Set<T> replace(T currentElement, T newElement);

Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/Stream.java
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,11 @@ public final Stream<T> peek(Consumer<? super T> action) {
}
}

@Override
public final Stream<T> tapEach(Consumer<? super T> action) {
return Collections.tapEach(this, action);
}

@Override
public final Stream<Stream<T>> permutations() {
if (isEmpty()) {
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/io/vavr/collection/Traversable.java
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,9 @@ default boolean nonEmpty() {
@Override
Traversable<T> peek(Consumer<? super T> action);


Traversable<T> tapEach(Consumer<? super T> action);

/**
* Calculates the product of this elements. Supported component types are {@code Byte}, {@code Double}, {@code Float},
* {@code Integer}, {@code Long}, {@code Short}, {@code BigInteger} and {@code BigDecimal}.
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/Tree.java
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,11 @@ public final Tree<T> peek(Consumer<? super T> action) {
return this;
}

@Override
public final Tree<T> tapEach(Consumer<? super T> action) {
return Collections.tapEach(this, action);
}

@Override
public final Tree<T> replace(T currentElement, T newElement) {
if (isEmpty()) {
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/TreeMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,11 @@ public TreeMap<K, V> peek(Consumer<? super Tuple2<K, V>> action) {
return Maps.peek(this, action);
}

@Override
public TreeMap<K, V> tapEach(Consumer<? super Tuple2<K, V>> action) {
return Collections.tapEach(this, action);
}

@Override
public <U extends V> TreeMap<K, V> put(K key, U value, BiFunction<? super V, ? super U, ? extends V> merge) {
return Maps.put(this, key, value, merge);
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/TreeSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,11 @@ public TreeSet<T> peek(Consumer<? super T> action) {
return this;
}

@Override
public TreeSet<T> tapEach(Consumer<? super T> action) {
return Collections.tapEach(this, action);
}

@Override
public TreeSet<T> remove(T element) {
return new TreeSet<>(tree.delete(element));
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/vavr/collection/Vector.java
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,11 @@ public Vector<T> peek(Consumer<? super T> action) {
}
return this;
}

@Override
public Vector<T> tapEach(Consumer<? super T> action) {
return Collections.tapEach(this, action);
}

@Override
public Vector<Vector<T>> permutations() {
Expand Down
66 changes: 66 additions & 0 deletions src/test/java/io/vavr/collection/AbstractTraversableTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@

public abstract class AbstractTraversableTest extends AbstractValueTest {

protected final boolean isLazy() {
return empty().isLazy();
}

protected final boolean isTraversableAgain() {
return empty().isTraversableAgain();
}
Expand All @@ -50,6 +54,10 @@ protected final boolean isOrdered() {
return empty().isOrdered();
}

protected final boolean isSequential() {
return empty().isSequential();
}

protected abstract <T> Collector<T, ArrayList<T>, ? extends Traversable<T>> collector();

@Override
Expand Down Expand Up @@ -2844,6 +2852,64 @@ public void ofAllShouldReturnTheSingletonEmpty() {
assertThat(ofAll(io.vavr.collection.Iterator.empty())).isSameAs(empty());
}

// eager traversable shall apply action immediately

@Test
public void tapEachShouldThrowIfActionIsNull() {
assertThatThrownBy(() -> of("one", "two", "three").tapEach(null))
.isInstanceOf(NullPointerException.class);
}
@Test
public void tapEachShouldDoNothingOnLazyTraversableUntilElementsMaterialized() {
// test is meaningful for lazy Traversable<T> only
if (isLazy()) {
String errorMessage = "tapEach() must not call effect on lazy Traversable<T> before its elements are materialized";
of("one", "two", "three").tapEach(__ -> fail(errorMessage)); // note: no elements materialized yet
}
}

@Test
public void tapEachShouldDoNothingOnEmptyTraversable() {
empty()
.tapEach(__ -> fail("tapEach() must never call effect on empty Traversable<T>"))
.forEach(__ -> {}); // enforcing all elements to materialize
}

@Test
public void tapEachShouldApplyEffectToEveryElementExactlyOnce() {
String[] values = {"one", "two", "three"};
// some collections may not guarantee the order of effect applications,
// so here we'll assert the 'exactly once' property only
java.util.Map<String, Integer> effectCallCounter = new java.util.HashMap<>();

// instantiating a Traversable<T> from elements provided, tappping each element
// and forcing all of its elements to materialize
of(values)
.tapEach(element -> effectCallCounter.compute(element, (__, v) -> v == null ? 1 : v + 1))
.forEach(__ -> {});

assertThat(effectCallCounter.get("one")).isEqualTo(1); // effect called for value "one" exactly once
assertThat(effectCallCounter.get("two")).isEqualTo(1); // ... for value "two" exactly once
assertThat(effectCallCounter.get("three")).isEqualTo(1); // ... for value "three" exactly once
assertThat(effectCallCounter.size()).isEqualTo(values.length);
}

@Test
public void tapEachShouldApplyEffectToEveryElementInOrder() {
// testing effects order is for ordered Traversable<T> only
if (this.isSequential()) {
String[] values = {"one", "two", "three"};
java.util.List<String> effectCallLog = new java.util.ArrayList<>();
// instantiating a Traversable<T> from elements provided, tappping each element
// and forcing all of its elements to materialize
of(values)
.tapEach(element -> effectCallLog.add(element))
.forEach(__ -> {});

assertThat(effectCallLog).isEqualTo(Arrays.asList(values));
}
}

private void testCollector(Runnable test) {
if (isTraversableAgain()) {
test.run();
Expand Down
5 changes: 5 additions & 0 deletions src/test/java/io/vavr/collection/IntMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ public IntMap<T> peek(Consumer<? super T> action) {
return this;
}

@Override
public IntMap<T> tapEach(Consumer<? super T> action) {
return Collections.tapEach(this, action);
}

@Override
public IntMap<T> replace(T currentElement, T newElement) {
final Option<Tuple2<Integer, T>> currentEntryOpt = original.find(e -> e._2.equals(currentElement));
Expand Down
5 changes: 5 additions & 0 deletions src/test/java/io/vavr/collection/IntMultimap.java
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,11 @@ public IntMultimap<T> peek(Consumer<? super T> action) {
return this;
}

@Override
public IntMultimap<T> tapEach(Consumer<? super T> action) {
return Collections.tapEach(this, action);
}

@Override
public IntMultimap<T> replace(T currentElement, T newElement) {
final Option<Tuple2<Integer, T>> currentEntryOpt = original.find(e -> e._2.equals(currentElement));
Expand Down