From 6ea1196759e2adf2bdb36e2f39815914021b3362 Mon Sep 17 00:00:00 2001 From: Pavel Mitrofanov <195997+nblxa@users.noreply.github.com> Date: Sun, 16 Feb 2020 18:39:07 +0100 Subject: [PATCH 1/4] Implement map --- .../java/io/github/nblxa/cons/ConsList.java | 14 ++++++ .../io/github/nblxa/cons/ConsListImpl.java | 7 +++ .../java/io/github/nblxa/cons/ConsUtil.java | 14 ++++++ .../github/nblxa/cons/DoubleConsListImpl.java | 7 +++ .../io/github/nblxa/cons/IntConsListImpl.java | 7 +++ .../github/nblxa/cons/LongConsListImpl.java | 7 +++ .../main/java/io/github/nblxa/cons/Nil.java | 8 ++++ .../io/github/nblxa/cons/ConsListTest.java | 48 +++++++++++++++++++ 8 files changed, 112 insertions(+) diff --git a/cons-list/src/main/java/io/github/nblxa/cons/ConsList.java b/cons-list/src/main/java/io/github/nblxa/cons/ConsList.java index a17460a..2834493 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/ConsList.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/ConsList.java @@ -6,6 +6,7 @@ import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; import java.util.*; +import java.util.function.Function; import java.util.stream.Collector; import java.util.stream.Collectors; @@ -669,4 +670,17 @@ static DoubleConsList concat(@NonNull DoubleConsList first, @Non */ @NonNull ConsList reverse(); + + /** + * Eager implementation of the map method that applies mappingFunction + * to all elements of the ConsList. + * + * The resulting new list has the same order of elements. + * + * @param mappingFunction the mapping function to be applied to all elements + * @param new list&s element type + * @return new resulting list + */ + @NonNull + ConsList map(@NonNull Function mappingFunction); } diff --git a/cons-list/src/main/java/io/github/nblxa/cons/ConsListImpl.java b/cons-list/src/main/java/io/github/nblxa/cons/ConsListImpl.java index 242ce5b..2637371 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/ConsListImpl.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/ConsListImpl.java @@ -9,6 +9,7 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.*; +import java.util.function.Function; @Immutable @ThreadSafe @@ -46,6 +47,12 @@ public ConsList reverse() { return result; } + @NonNull + @Override + public ConsList map(@NonNull Function mappingFunction) { + return ConsUtil.map(this, mappingFunction); + } + @Override public boolean isEmpty() { return false; diff --git a/cons-list/src/main/java/io/github/nblxa/cons/ConsUtil.java b/cons-list/src/main/java/io/github/nblxa/cons/ConsUtil.java index ac5825c..943e824 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/ConsUtil.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/ConsUtil.java @@ -1,7 +1,10 @@ package io.github.nblxa.cons; +import edu.umd.cs.findbugs.annotations.NonNull; + import java.util.Objects; import java.util.Spliterator; +import java.util.function.Function; final class ConsUtil { private ConsUtil() { @@ -60,4 +63,15 @@ static boolean haveEqualElements(DoubleConsList first, DoubleConsList< } return first == Nil.INSTANCE && second == Nil.INSTANCE; } + + @NonNull + static ConsList map(@NonNull ConsList cons, @NonNull Function mappingFunction) { + ConsList result = ConsList.nil(); + while (cons != Nil.INSTANCE) { + T t = mappingFunction.apply(cons.head()); + result = new ConsListImpl<>(t, result); + cons = cons.tail(); + } + return result.reverse(); + } } diff --git a/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsListImpl.java b/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsListImpl.java index cfea251..90bda0b 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsListImpl.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsListImpl.java @@ -9,6 +9,7 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.*; +import java.util.function.Function; import java.util.stream.DoubleStream; import java.util.stream.StreamSupport; @@ -67,6 +68,12 @@ public ConsList reverse() { return doubleReverse(); } + @NonNull + @Override + public ConsList map(@NonNull Function mappingFunction) { + return ConsUtil.map(this, mappingFunction); + } + @Override public boolean isEmpty() { return false; diff --git a/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java b/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java index 617479b..62501de 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java @@ -9,6 +9,7 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.*; +import java.util.function.Function; import java.util.stream.IntStream; import java.util.stream.StreamSupport; @@ -67,6 +68,12 @@ public ConsList reverse() { return intReverse(); } + @NonNull + @Override + public ConsList map(@NonNull Function mappingFunction) { + return ConsUtil.map(this, mappingFunction); + } + @Override public boolean isEmpty() { return false; diff --git a/cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java b/cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java index 9d222f4..b68cbcc 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java @@ -9,6 +9,7 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.*; +import java.util.function.Function; import java.util.stream.LongStream; import java.util.stream.StreamSupport; @@ -67,6 +68,12 @@ public ConsList reverse() { return longReverse(); } + @NonNull + @Override + public ConsList map(@NonNull Function mappingFunction) { + return ConsUtil.map(this, mappingFunction); + } + @Override public boolean isEmpty() { return false; diff --git a/cons-list/src/main/java/io/github/nblxa/cons/Nil.java b/cons-list/src/main/java/io/github/nblxa/cons/Nil.java index 4881c3f..2b76368 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/Nil.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/Nil.java @@ -6,6 +6,7 @@ import javax.annotation.concurrent.ThreadSafe; import java.io.Serializable; import java.util.*; +import java.util.function.Function; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; @@ -44,6 +45,13 @@ public Nil reverse() { return this; } + @NonNull + @Override + @SuppressWarnings("unchecked") + public Nil map(@NonNull Function mappingFunction) { + return (Nil) this; + } + @Override public int intHead() { throw new NoSuchElementException(); diff --git a/cons-list/src/test/java/io/github/nblxa/cons/ConsListTest.java b/cons-list/src/test/java/io/github/nblxa/cons/ConsListTest.java index a8eb9c8..028eb11 100644 --- a/cons-list/src/test/java/io/github/nblxa/cons/ConsListTest.java +++ b/cons-list/src/test/java/io/github/nblxa/cons/ConsListTest.java @@ -355,4 +355,52 @@ public void consUtil() throws NoSuchMethodException { } } } + + @Test + public void map_producesExpectedResult() { + ConsList integers = list(3, 14, 12, 92, 6); + ConsList strings = integers.map(Object::toString); + assertThat(strings) + .containsExactly("3", "14", "12", "92", "6"); + } + + @Test + public void map_isEager() { + List events = new ArrayList<>(); + + ConsList integers = list(3, 14) + .map(e -> { + events.add("Map 1 Element " + e); + return e; + }) + .map(e -> { + events.add("Map 2 Element " + e); + return e; + }); + + assertThat(events) + .containsExactly( + "Map 1 Element 3", "Map 1 Element 14", "Map 2 Element 3", "Map 2 Element 14"); + } + + @Test + public void streamMap_isLazy() { + List events = new ArrayList<>(); + + ConsList integers = list(3, 14) + .stream() + .map(e -> { + events.add("Map 1 Element " + e); + return e; + }) + .map(e -> { + events.add("Map 2 Element " + e); + return e; + }) + .collect(ConsList.toConsCollector()); + + assertThat(events) + .containsExactly( + "Map 1 Element 3", "Map 2 Element 3", "Map 1 Element 14", "Map 2 Element 14"); + } } From 3cc72a843d965b6b4f322c87148929e22381f405 Mon Sep 17 00:00:00 2001 From: Pavel Mitrofanov <195997+nblxa@users.noreply.github.com> Date: Sun, 16 Feb 2020 20:00:32 +0100 Subject: [PATCH 2/4] Implement doubleMap --- .../java/io/github/nblxa/cons/ConsList.java | 6 +-- .../io/github/nblxa/cons/ConsListImpl.java | 4 +- .../io/github/nblxa/cons/DoubleConsList.java | 13 +++++ .../github/nblxa/cons/DoubleConsListImpl.java | 18 ++++++- .../io/github/nblxa/cons/IntConsListImpl.java | 4 +- .../github/nblxa/cons/LongConsListImpl.java | 4 +- .../main/java/io/github/nblxa/cons/Nil.java | 9 +++- .../io/github/nblxa/cons/ConsListTest.java | 4 +- .../github/nblxa/cons/DoubleConsListTest.java | 49 +++++++++++++++++++ 9 files changed, 97 insertions(+), 14 deletions(-) diff --git a/cons-list/src/main/java/io/github/nblxa/cons/ConsList.java b/cons-list/src/main/java/io/github/nblxa/cons/ConsList.java index 2834493..0ff2030 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/ConsList.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/ConsList.java @@ -672,15 +672,15 @@ static DoubleConsList concat(@NonNull DoubleConsList first, @Non ConsList reverse(); /** - * Eager implementation of the map method that applies mappingFunction + * Eager implementation of the map method that applies mapper * to all elements of the ConsList. * * The resulting new list has the same order of elements. * - * @param mappingFunction the mapping function to be applied to all elements + * @param mapper the mapping function to be applied to all elements * @param new list&s element type * @return new resulting list */ @NonNull - ConsList map(@NonNull Function mappingFunction); + ConsList map(@NonNull Function mapper); } diff --git a/cons-list/src/main/java/io/github/nblxa/cons/ConsListImpl.java b/cons-list/src/main/java/io/github/nblxa/cons/ConsListImpl.java index 2637371..003d3ca 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/ConsListImpl.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/ConsListImpl.java @@ -49,8 +49,8 @@ public ConsList reverse() { @NonNull @Override - public ConsList map(@NonNull Function mappingFunction) { - return ConsUtil.map(this, mappingFunction); + public ConsList map(@NonNull Function mapper) { + return ConsUtil.map(this, mapper); } @Override diff --git a/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsList.java b/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsList.java index df881bc..d5985a6 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsList.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsList.java @@ -7,6 +7,7 @@ import java.util.NoSuchElementException; import java.util.PrimitiveIterator; import java.util.Spliterator; +import java.util.function.DoubleUnaryOperator; import java.util.stream.DoubleStream; @Immutable @@ -63,4 +64,16 @@ public interface DoubleConsList extends ConsList { */ @NonNull DoubleStream doubleStream(); + + /** + * Eager implementation of the map method that applies mapper + * to all elements of the DoubleConsList. + * + * The resulting new list has the same order of elements. + * + * @param mapper the mapping function to be applied to all elements + * @return new resulting list + */ + @NonNull + DoubleConsList doubleMap(@NonNull DoubleUnaryOperator mapper); } diff --git a/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsListImpl.java b/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsListImpl.java index 90bda0b..64a648a 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsListImpl.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsListImpl.java @@ -9,6 +9,7 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.*; +import java.util.function.DoubleUnaryOperator; import java.util.function.Function; import java.util.stream.DoubleStream; import java.util.stream.StreamSupport; @@ -50,6 +51,19 @@ public DoubleConsList doubleReverse() { return result; } + @NonNull + @Override + public DoubleConsList doubleMap(@NonNull DoubleUnaryOperator mapper) { + DoubleConsList result = ConsList.nil(); + DoubleConsList cons = this; + while (cons != Nil.INSTANCE) { + double d = mapper.applyAsDouble(cons.doubleHead()); + result = new DoubleConsListImpl(d, result); + cons = cons.doubleTail(); + } + return result.doubleReverse(); + } + @NonNull @Override public Double head() { @@ -70,8 +84,8 @@ public ConsList reverse() { @NonNull @Override - public ConsList map(@NonNull Function mappingFunction) { - return ConsUtil.map(this, mappingFunction); + public ConsList map(@NonNull Function mapper) { + return ConsUtil.map(this, mapper); } @Override diff --git a/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java b/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java index 62501de..424fa3b 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java @@ -70,8 +70,8 @@ public ConsList reverse() { @NonNull @Override - public ConsList map(@NonNull Function mappingFunction) { - return ConsUtil.map(this, mappingFunction); + public ConsList map(@NonNull Function mapper) { + return ConsUtil.map(this, mapper); } @Override diff --git a/cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java b/cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java index b68cbcc..2f543ea 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java @@ -70,8 +70,8 @@ public ConsList reverse() { @NonNull @Override - public ConsList map(@NonNull Function mappingFunction) { - return ConsUtil.map(this, mappingFunction); + public ConsList map(@NonNull Function mapper) { + return ConsUtil.map(this, mapper); } @Override diff --git a/cons-list/src/main/java/io/github/nblxa/cons/Nil.java b/cons-list/src/main/java/io/github/nblxa/cons/Nil.java index 2b76368..96b2412 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/Nil.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/Nil.java @@ -6,6 +6,7 @@ import javax.annotation.concurrent.ThreadSafe; import java.io.Serializable; import java.util.*; +import java.util.function.DoubleUnaryOperator; import java.util.function.Function; import java.util.stream.DoubleStream; import java.util.stream.IntStream; @@ -48,7 +49,7 @@ public Nil reverse() { @NonNull @Override @SuppressWarnings("unchecked") - public Nil map(@NonNull Function mappingFunction) { + public Nil map(@NonNull Function mapper) { return (Nil) this; } @@ -159,6 +160,12 @@ public DoubleConsList doubleReverse() { return this; } + @NonNull + @Override + public DoubleConsList doubleMap(@NonNull DoubleUnaryOperator mapper) { + return this; + } + @NonNull @Override public PrimitiveIterator.OfDouble doubleIterator() { diff --git a/cons-list/src/test/java/io/github/nblxa/cons/ConsListTest.java b/cons-list/src/test/java/io/github/nblxa/cons/ConsListTest.java index 028eb11..bdcb5fd 100644 --- a/cons-list/src/test/java/io/github/nblxa/cons/ConsListTest.java +++ b/cons-list/src/test/java/io/github/nblxa/cons/ConsListTest.java @@ -368,7 +368,7 @@ public void map_producesExpectedResult() { public void map_isEager() { List events = new ArrayList<>(); - ConsList integers = list(3, 14) + list(3, 14) .map(e -> { events.add("Map 1 Element " + e); return e; @@ -387,7 +387,7 @@ public void map_isEager() { public void streamMap_isLazy() { List events = new ArrayList<>(); - ConsList integers = list(3, 14) + list(3, 14) .stream() .map(e -> { events.add("Map 1 Element " + e); diff --git a/cons-list/src/test/java/io/github/nblxa/cons/DoubleConsListTest.java b/cons-list/src/test/java/io/github/nblxa/cons/DoubleConsListTest.java index 76bd331..3be0f03 100644 --- a/cons-list/src/test/java/io/github/nblxa/cons/DoubleConsListTest.java +++ b/cons-list/src/test/java/io/github/nblxa/cons/DoubleConsListTest.java @@ -319,4 +319,53 @@ public void concat_firstNullListArg_throwsException() { .hasNoCause() .hasMessage("Null concat argument at position 0"); } + + @Test + public void doubleMap_producesExpectedResult() { + DoubleConsList doubles = doubleList(3.1d, 14.1d, 12.1d, 92.1d, 6.1d); + DoubleConsList timesTwo = doubles.doubleMap(d -> d * 2); + assertThat(timesTwo) + .containsExactly(6.2d, 28.2d, 24.2d, 184.2d, 12.2d); + } + + @Test + public void doubleMap_isEager() { + List events = new ArrayList<>(); + + doubleList(3.1d, 14.1d) + .doubleMap(e -> { + events.add("Map 1 Element " + e); + return e; + }) + .doubleMap(e -> { + events.add("Map 2 Element " + e); + return e; + }); + + assertThat(events) + .containsExactly( + "Map 1 Element 3.1", "Map 1 Element 14.1", "Map 2 Element 3.1", "Map 2 Element 14.1"); + } + + @Test + public void doubleStreamMap_isLazy() { + List events = new ArrayList<>(); + + doubleList(3.1d, 14.1d) + .doubleStream() + .map(e -> { + events.add("Map 1 Element " + e); + return e; + }) + .map(e -> { + events.add("Map 2 Element " + e); + return e; + }) + .boxed() + .collect(ConsList.toConsCollector()); + + assertThat(events) + .containsExactly( + "Map 1 Element 3.1", "Map 2 Element 3.1", "Map 1 Element 14.1", "Map 2 Element 14.1"); + } } From 57c7fecd174a522dde7d110125cd40fc6303f6f0 Mon Sep 17 00:00:00 2001 From: Pavel Mitrofanov <195997+nblxa@users.noreply.github.com> Date: Sun, 16 Feb 2020 20:43:56 +0100 Subject: [PATCH 3/4] Implement intMap and longMap, fix minor issues: --- README.md | 8 +-- .../java/io/github/nblxa/cons/ConsList.java | 4 +- .../java/io/github/nblxa/cons/ConsUtil.java | 3 +- .../github/nblxa/cons/DoubleConsListImpl.java | 3 +- .../io/github/nblxa/cons/IntConsList.java | 13 ++++ .../io/github/nblxa/cons/IntConsListImpl.java | 14 +++++ .../io/github/nblxa/cons/LongConsList.java | 13 ++++ .../github/nblxa/cons/LongConsListImpl.java | 49 +++++++++------ .../main/java/io/github/nblxa/cons/Nil.java | 26 ++++++-- .../io/github/nblxa/cons/IntConsListTest.java | 41 ++++++++++++ .../github/nblxa/cons/LongConsListTest.java | 41 ++++++++++++ .../java/io/github/nblxa/cons/NilTest.java | 62 +++++++++++++++---- 12 files changed, 232 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index eeed8ae..efb5eb9 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ safeguarding against a `StackOverflowError`. Cons List, due to its simplicity and immutability, is an ideal data structure for multi-threaded processing of ordered collections of data. -Direct implementations however, suffer from heavy recsive calls +Direct implementations however, suffer from heavy recursive calls and may cause high stack memory consumption, if not more severe issues. This implementation fuses the power of the immutable cons list with the wide range of functionality offered by the Java Collections @@ -113,7 +113,7 @@ ConsList fruit = Arrays.stream(new String[] {"Apples", "Bananas", "Orang Being an immutable collection, `ConsList` lets one save resources on defensive copying where it would otherwise have been necessary for mutable collections, -such as `ArrayList`. +such as `ArrayList`. See [ConsListBenchmark.java](cons-list-jmh/src/main/java/io/github/nblxa/ConsListBenchmark.java). @@ -121,9 +121,9 @@ Specific problems like flattening a tree-like hierarchical structure can be solved more optimally with `ConsList`, however the trivial list-growth and iteration operations perform better using `java.util.ArrayList`. -Specialized implementations for primitive types, like the `IntConsList`, +Specialized implementations for primitive types, like the `IntConsList`, for instance, may outperform `ArrayList` in some benchmarks due -to the lack of boxing. +to the lack of boxing. Here are the benchmark results on the author's machine: diff --git a/cons-list/src/main/java/io/github/nblxa/cons/ConsList.java b/cons-list/src/main/java/io/github/nblxa/cons/ConsList.java index 0ff2030..e914d50 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/ConsList.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/ConsList.java @@ -222,7 +222,7 @@ static ConsList consList(@NonNull Iterable iterable) { @NonNull @SafeVarargs static ConsList concat(@NonNull ConsList first, @NonNull ConsList... rest) { - Objects.requireNonNull(first, "Null concat argument at position 0"); + Objects.requireNonNull(first, ConsUtil.MSG_NULL_CONCAT_ARG_AT_POS_0); Objects.requireNonNull(rest, ConsUtil.MSG_ARG_ARRAY_REST_IS_NULL); if (rest.length == 0) { return first; @@ -617,7 +617,7 @@ static DoubleConsList doubleConsList(@NonNull Iterable iterable) @NonNull @SafeVarargs static DoubleConsList concat(@NonNull DoubleConsList first, @NonNull DoubleConsList... rest) { - Objects.requireNonNull(first, "Null concat argument at position 0"); + Objects.requireNonNull(first, ConsUtil.MSG_NULL_CONCAT_ARG_AT_POS_0); Objects.requireNonNull(rest, ConsUtil.MSG_ARG_ARRAY_REST_IS_NULL); if (rest.length == 0) { return first; diff --git a/cons-list/src/main/java/io/github/nblxa/cons/ConsUtil.java b/cons-list/src/main/java/io/github/nblxa/cons/ConsUtil.java index 943e824..e12bb72 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/ConsUtil.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/ConsUtil.java @@ -68,8 +68,7 @@ static boolean haveEqualElements(DoubleConsList first, DoubleConsList< static ConsList map(@NonNull ConsList cons, @NonNull Function mappingFunction) { ConsList result = ConsList.nil(); while (cons != Nil.INSTANCE) { - T t = mappingFunction.apply(cons.head()); - result = new ConsListImpl<>(t, result); + result = new ConsListImpl<>(mappingFunction.apply(cons.head()), result); cons = cons.tail(); } return result.reverse(); diff --git a/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsListImpl.java b/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsListImpl.java index 64a648a..99b126a 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsListImpl.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/DoubleConsListImpl.java @@ -57,8 +57,7 @@ public DoubleConsList doubleMap(@NonNull DoubleUnaryOperator mapper) { DoubleConsList result = ConsList.nil(); DoubleConsList cons = this; while (cons != Nil.INSTANCE) { - double d = mapper.applyAsDouble(cons.doubleHead()); - result = new DoubleConsListImpl(d, result); + result = new DoubleConsListImpl(mapper.applyAsDouble(cons.doubleHead()), result); cons = cons.doubleTail(); } return result.doubleReverse(); diff --git a/cons-list/src/main/java/io/github/nblxa/cons/IntConsList.java b/cons-list/src/main/java/io/github/nblxa/cons/IntConsList.java index be9fc14..9d7d5f3 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/IntConsList.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/IntConsList.java @@ -7,6 +7,7 @@ import java.util.NoSuchElementException; import java.util.PrimitiveIterator; import java.util.Spliterator; +import java.util.function.IntUnaryOperator; import java.util.stream.IntStream; @Immutable @@ -63,4 +64,16 @@ public interface IntConsList extends ConsList { */ @NonNull IntStream intStream(); + + /** + * Eager implementation of the map method that applies mapper + * to all elements of the IntConsList. + * + * The resulting new list has the same order of elements. + * + * @param mapper the mapping function to be applied to all elements + * @return new resulting list + */ + @NonNull + IntConsList intMap(@NonNull IntUnaryOperator mapper); } diff --git a/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java b/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java index 424fa3b..acdb54c 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java @@ -9,7 +9,9 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.*; +import java.util.function.DoubleUnaryOperator; import java.util.function.Function; +import java.util.function.IntUnaryOperator; import java.util.stream.IntStream; import java.util.stream.StreamSupport; @@ -50,6 +52,18 @@ public IntConsList intReverse() { return result; } + @NonNull + @Override + public IntConsList intMap(@NonNull IntUnaryOperator mapper) { + IntConsList result = ConsList.nil(); + IntConsList cons = this; + while (cons != Nil.INSTANCE) { + result = new IntConsListImpl(mapper.applyAsInt(cons.intHead()), result); + cons = cons.intTail(); + } + return result.intReverse(); + } + @NonNull @Override public Integer head() { diff --git a/cons-list/src/main/java/io/github/nblxa/cons/LongConsList.java b/cons-list/src/main/java/io/github/nblxa/cons/LongConsList.java index c9eb589..26dc6c3 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/LongConsList.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/LongConsList.java @@ -7,6 +7,7 @@ import java.util.NoSuchElementException; import java.util.PrimitiveIterator; import java.util.Spliterator; +import java.util.function.LongUnaryOperator; import java.util.stream.LongStream; @Immutable @@ -63,4 +64,16 @@ public interface LongConsList extends ConsList { */ @NonNull LongStream longStream(); + + /** + * Eager implementation of the map method that applies mapper + * to all elements of the LongConsList. + * + * The resulting new list has the same order of elements. + * + * @param mapper the mapping function to be applied to all elements + * @return new resulting list + */ + @NonNull + LongConsList longMap(@NonNull LongUnaryOperator mapper); } diff --git a/cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java b/cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java index 2f543ea..f9338f9 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java @@ -10,6 +10,7 @@ import java.io.Serializable; import java.util.*; import java.util.function.Function; +import java.util.function.LongUnaryOperator; import java.util.stream.LongStream; import java.util.stream.StreamSupport; @@ -50,6 +51,36 @@ public LongConsList longReverse() { return result; } + @NonNull + @Override + public PrimitiveIterator.OfLong longIterator() { + return new LongConsIterator(this); + } + + @NonNull + @Override + public Spliterator.OfLong longSpliterator() { + return Spliterators.spliteratorUnknownSize(longIterator(), ConsUtil.SPLITERATOR_CHARACTERISTICS); + } + + @NonNull + @Override + public LongStream longStream() { + return StreamSupport.longStream(longSpliterator(), false); + } + + @NonNull + @Override + public LongConsList longMap(@NonNull LongUnaryOperator mapper) { + LongConsList result = ConsList.nil(); + LongConsList cons = this; + while (cons != Nil.INSTANCE) { + result = new LongConsListImpl(mapper.applyAsLong(cons.longHead()), result); + cons = cons.longTail(); + } + return result.longReverse(); + } + @NonNull @Override public Long head() { @@ -102,24 +133,6 @@ public Spliterator spliterator() { return longSpliterator(); } - @NonNull - @Override - public PrimitiveIterator.OfLong longIterator() { - return new LongConsIterator(this); - } - - @NonNull - @Override - public Spliterator.OfLong longSpliterator() { - return Spliterators.spliteratorUnknownSize(longIterator(), ConsUtil.SPLITERATOR_CHARACTERISTICS); - } - - @NonNull - @Override - public LongStream longStream() { - return StreamSupport.longStream(longSpliterator(), false); - } - @Override public final boolean equals(Object o) { if (this == o) { diff --git a/cons-list/src/main/java/io/github/nblxa/cons/Nil.java b/cons-list/src/main/java/io/github/nblxa/cons/Nil.java index 96b2412..e879497 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/Nil.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/Nil.java @@ -8,6 +8,8 @@ import java.util.*; import java.util.function.DoubleUnaryOperator; import java.util.function.Function; +import java.util.function.IntUnaryOperator; +import java.util.function.LongUnaryOperator; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; @@ -98,6 +100,12 @@ public IntStream intStream() { return IntStream.empty(); } + @NonNull + @Override + public IntConsList intMap(@NonNull IntUnaryOperator mapper) { + return this; + } + @Override public long longHead() { throw new NoSuchElementException(); @@ -143,6 +151,12 @@ public LongStream longStream() { return LongStream.empty(); } + @NonNull + @Override + public LongConsList longMap(@NonNull LongUnaryOperator mapper) { + return this; + } + @Override public double doubleHead() { throw new NoSuchElementException(); @@ -160,12 +174,6 @@ public DoubleConsList doubleReverse() { return this; } - @NonNull - @Override - public DoubleConsList doubleMap(@NonNull DoubleUnaryOperator mapper) { - return this; - } - @NonNull @Override public PrimitiveIterator.OfDouble doubleIterator() { @@ -194,6 +202,12 @@ public DoubleStream doubleStream() { return DoubleStream.empty(); } + @NonNull + @Override + public DoubleConsList doubleMap(@NonNull DoubleUnaryOperator mapper) { + return this; + } + @Override public boolean isEmpty() { return true; diff --git a/cons-list/src/test/java/io/github/nblxa/cons/IntConsListTest.java b/cons-list/src/test/java/io/github/nblxa/cons/IntConsListTest.java index 3fcbefa..de4837d 100644 --- a/cons-list/src/test/java/io/github/nblxa/cons/IntConsListTest.java +++ b/cons-list/src/test/java/io/github/nblxa/cons/IntConsListTest.java @@ -319,4 +319,45 @@ public void concat_firstNullListArg_throwsException() { .hasNoCause() .hasMessage("Null concat argument at position 0"); } + + @Test + public void intMap_isEager() { + List events = new ArrayList<>(); + + intList(3, 14) + .intMap(e -> { + events.add("Map 1 Element " + e); + return e; + }) + .intMap(e -> { + events.add("Map 2 Element " + e); + return e; + }); + + assertThat(events) + .containsExactly( + "Map 1 Element 3", "Map 1 Element 14", "Map 2 Element 3", "Map 2 Element 14"); + } + + @Test + public void intStreamMap_isLazy() { + List events = new ArrayList<>(); + + intList(3, 14) + .intStream() + .map(e -> { + events.add("Map 1 Element " + e); + return e; + }) + .map(e -> { + events.add("Map 2 Element " + e); + return e; + }) + .boxed() + .collect(ConsList.toConsCollector()); + + assertThat(events) + .containsExactly( + "Map 1 Element 3", "Map 2 Element 3", "Map 1 Element 14", "Map 2 Element 14"); + } } diff --git a/cons-list/src/test/java/io/github/nblxa/cons/LongConsListTest.java b/cons-list/src/test/java/io/github/nblxa/cons/LongConsListTest.java index 025907c..4437716 100644 --- a/cons-list/src/test/java/io/github/nblxa/cons/LongConsListTest.java +++ b/cons-list/src/test/java/io/github/nblxa/cons/LongConsListTest.java @@ -319,4 +319,45 @@ public void concat_firstNullListArg_throwsException() { .hasNoCause() .hasMessage("Null concat argument at position 0"); } + + @Test + public void longMap_isEager() { + List events = new ArrayList<>(); + + longList(3L, 14L) + .longMap(e -> { + events.add("Map 1 Element " + e); + return e; + }) + .longMap(e -> { + events.add("Map 2 Element " + e); + return e; + }); + + assertThat(events) + .containsExactly( + "Map 1 Element 3", "Map 1 Element 14", "Map 2 Element 3", "Map 2 Element 14"); + } + + @Test + public void longStreamMap_isLazy() { + List events = new ArrayList<>(); + + longList(3L, 14L) + .longStream() + .map(e -> { + events.add("Map 1 Element " + e); + return e; + }) + .map(e -> { + events.add("Map 2 Element " + e); + return e; + }) + .boxed() + .collect(ConsList.toConsCollector()); + + assertThat(events) + .containsExactly( + "Map 1 Element 3", "Map 2 Element 3", "Map 1 Element 14", "Map 2 Element 14"); + } } diff --git a/cons-list/src/test/java/io/github/nblxa/cons/NilTest.java b/cons-list/src/test/java/io/github/nblxa/cons/NilTest.java index 08d8080..b19a27f 100644 --- a/cons-list/src/test/java/io/github/nblxa/cons/NilTest.java +++ b/cons-list/src/test/java/io/github/nblxa/cons/NilTest.java @@ -25,6 +25,7 @@ public void testHashCode() { @Test public void testSingleton() throws NoSuchMethodException { + @SuppressWarnings("rawtypes") Constructor constructor = Nil.class.getDeclaredConstructor(); boolean isAccessible = constructor.isAccessible(); try { @@ -95,7 +96,8 @@ public void testNilDoubleIterator() { @Test public void testNilSpliterator() { Spliterator spliter = nil().spliterator(); - assertThat(spliter.getExactSizeIfKnown()).isEqualTo(0L); + assertThat(spliter.getExactSizeIfKnown()) + .isEqualTo(0L); } @Test @@ -107,13 +109,15 @@ public void testNilIntSpliterator() { @Test public void testNilLongSpliterator() { Spliterator.OfLong spliter = nil().longSpliterator(); - assertThat(spliter.getExactSizeIfKnown()).isEqualTo(0L); + assertThat(spliter.getExactSizeIfKnown()) + .isEqualTo(0L); } @Test public void testNilDoubleSpliterator() { Spliterator.OfDouble spliter = nil().doubleSpliterator(); - assertThat(spliter.getExactSizeIfKnown()).isEqualTo(0L); + assertThat(spliter.getExactSizeIfKnown()) + .isEqualTo(0L); } @Test @@ -121,7 +125,8 @@ public void testNilStream() { List list = nil() .stream() .collect(Collectors.toList()); - assertThat(list).isEmpty(); + assertThat(list) + .isEmpty(); } @Test @@ -130,7 +135,8 @@ public void testNilIntStream() { .intStream() .boxed() .collect(Collectors.toList()); - assertThat(list).isEmpty(); + assertThat(list) + .isEmpty(); } @Test @@ -139,7 +145,8 @@ public void testNilLongStream() { .longStream() .boxed() .collect(Collectors.toList()); - assertThat(list).isEmpty(); + assertThat(list) + .isEmpty(); } @Test @@ -148,30 +155,63 @@ public void testNilDoubleStream() { .doubleStream() .boxed() .collect(Collectors.toList()); - assertThat(list).isEmpty(); + assertThat(list) + .isEmpty(); } @Test public void testReverse() { ConsList nil = nil(); - assertThat(nil.reverse()).isEqualTo(nil); + assertThat(nil.reverse()) + .isSameAs(nil); } @Test public void testIntReverse() { IntConsList nil = nil(); - assertThat(nil.intReverse()).isEqualTo(nil); + assertThat(nil.intReverse()) + .isSameAs(nil); } @Test public void testLongReverse() { LongConsList nil = nil(); - assertThat(nil.longReverse()).isEqualTo(nil); + assertThat(nil.longReverse()) + .isSameAs(nil); } @Test public void testDoubleReverse() { DoubleConsList nil = nil(); - assertThat(nil.doubleReverse()).isEqualTo(nil); + assertThat(nil.doubleReverse()) + .isSameAs(nil); + } + + @Test + public void testMap() { + ConsList nil = nil(); + assertThat(nil.map(Object::toString)) + .isSameAs(nil); + } + + @Test + public void testIntMap() { + IntConsList nil = nil(); + assertThat(nil.intMap(i -> i + 1)) + .isSameAs(nil); + } + + @Test + public void testLongMap() { + LongConsList nil = nil(); + assertThat(nil.longMap(i -> i + 1L)) + .isSameAs(nil); + } + + @Test + public void testDoubleMap() { + DoubleConsList nil = nil(); + assertThat(nil.doubleMap(i -> i + 1d)) + .isSameAs(nil); } } From 616562c0da990a237ac0c89373cc93479b3c4b79 Mon Sep 17 00:00:00 2001 From: Pavel Mitrofanov <195997+nblxa@users.noreply.github.com> Date: Sun, 16 Feb 2020 21:18:58 +0100 Subject: [PATCH 4/4] Improve test coverage --- .../io/github/nblxa/cons/IntConsListImpl.java | 1 - .../github/nblxa/cons/DoubleConsListTest.java | 27 ++++++++++++++ .../io/github/nblxa/cons/IntConsListTest.java | 35 +++++++++++++++++++ .../github/nblxa/cons/LongConsListTest.java | 35 +++++++++++++++++++ .../io/github/nblxa/cons/NilEqualsTest.java | 9 +++-- .../java/io/github/nblxa/cons/NilTest.java | 16 ++++----- 6 files changed, 109 insertions(+), 14 deletions(-) diff --git a/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java b/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java index acdb54c..a187919 100644 --- a/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java +++ b/cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java @@ -9,7 +9,6 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.*; -import java.util.function.DoubleUnaryOperator; import java.util.function.Function; import java.util.function.IntUnaryOperator; import java.util.stream.IntStream; diff --git a/cons-list/src/test/java/io/github/nblxa/cons/DoubleConsListTest.java b/cons-list/src/test/java/io/github/nblxa/cons/DoubleConsListTest.java index 3be0f03..4c2b263 100644 --- a/cons-list/src/test/java/io/github/nblxa/cons/DoubleConsListTest.java +++ b/cons-list/src/test/java/io/github/nblxa/cons/DoubleConsListTest.java @@ -320,6 +320,33 @@ public void concat_firstNullListArg_throwsException() { .hasMessage("Null concat argument at position 0"); } + @Test + public void map_producesExpectedResult() { + ConsList doubles = doubleList(3.1d, 14.1d, 12.1d, 92.1d, 6.1d); + ConsList timesTwo = doubles.map(d -> d * 2); + assertThat(timesTwo) + .containsExactly(6.2d, 28.2d, 24.2d, 184.2d, 12.2d); + } + + @Test + public void map_isEager() { + List events = new ArrayList<>(); + + doubleList(3.1d, 14.1d) + .map(e -> { + events.add("Map 1 Element " + e); + return e; + }) + .map(e -> { + events.add("Map 2 Element " + e); + return e; + }); + + assertThat(events) + .containsExactly( + "Map 1 Element 3.1", "Map 1 Element 14.1", "Map 2 Element 3.1", "Map 2 Element 14.1"); + } + @Test public void doubleMap_producesExpectedResult() { DoubleConsList doubles = doubleList(3.1d, 14.1d, 12.1d, 92.1d, 6.1d); diff --git a/cons-list/src/test/java/io/github/nblxa/cons/IntConsListTest.java b/cons-list/src/test/java/io/github/nblxa/cons/IntConsListTest.java index de4837d..2b123a2 100644 --- a/cons-list/src/test/java/io/github/nblxa/cons/IntConsListTest.java +++ b/cons-list/src/test/java/io/github/nblxa/cons/IntConsListTest.java @@ -320,6 +320,41 @@ public void concat_firstNullListArg_throwsException() { .hasMessage("Null concat argument at position 0"); } + @Test + public void map_producesExpectedResult() { + ConsList integers = intList(3, 14, 12, 92, 6); + ConsList timesTwo = integers.map(d -> d * 2); + assertThat(timesTwo) + .containsExactly(6, 28, 24, 184, 12); + } + + @Test + public void map_isEager() { + List events = new ArrayList<>(); + + intList(3, 14) + .map(e -> { + events.add("Map 1 Element " + e); + return e; + }) + .map(e -> { + events.add("Map 2 Element " + e); + return e; + }); + + assertThat(events) + .containsExactly( + "Map 1 Element 3", "Map 1 Element 14", "Map 2 Element 3", "Map 2 Element 14"); + } + + @Test + public void intMap_producesExpectedResult() { + IntConsList integers = intList(3, 14, 12, 92, 6); + IntConsList timesTwo = integers.intMap(d -> d * 2); + assertThat(timesTwo) + .containsExactly(6, 28, 24, 184, 12); + } + @Test public void intMap_isEager() { List events = new ArrayList<>(); diff --git a/cons-list/src/test/java/io/github/nblxa/cons/LongConsListTest.java b/cons-list/src/test/java/io/github/nblxa/cons/LongConsListTest.java index 4437716..9868aae 100644 --- a/cons-list/src/test/java/io/github/nblxa/cons/LongConsListTest.java +++ b/cons-list/src/test/java/io/github/nblxa/cons/LongConsListTest.java @@ -320,6 +320,41 @@ public void concat_firstNullListArg_throwsException() { .hasMessage("Null concat argument at position 0"); } + @Test + public void map_producesExpectedResult() { + ConsList longs = longList(3L, 14L, 12L, 92L, 6L); + ConsList timesTwo = longs.map(d -> d * 2); + assertThat(timesTwo) + .containsExactly(6L, 28L, 24L, 184L, 12L); + } + + @Test + public void map_isEager() { + List events = new ArrayList<>(); + + longList(3L, 14L) + .map(e -> { + events.add("Map 1 Element " + e); + return e; + }) + .map(e -> { + events.add("Map 2 Element " + e); + return e; + }); + + assertThat(events) + .containsExactly( + "Map 1 Element 3", "Map 1 Element 14", "Map 2 Element 3", "Map 2 Element 14"); + } + + @Test + public void longMap_producesExpectedResult() { + LongConsList longs = longList(3L, 14L, 12L, 92L, 6L); + LongConsList timesTwo = longs.longMap(d -> d * 2); + assertThat(timesTwo) + .containsExactly(6L, 28L, 24L, 184L, 12L); + } + @Test public void longMap_isEager() { List events = new ArrayList<>(); diff --git a/cons-list/src/test/java/io/github/nblxa/cons/NilEqualsTest.java b/cons-list/src/test/java/io/github/nblxa/cons/NilEqualsTest.java index 59bf59b..19a55d2 100644 --- a/cons-list/src/test/java/io/github/nblxa/cons/NilEqualsTest.java +++ b/cons-list/src/test/java/io/github/nblxa/cons/NilEqualsTest.java @@ -4,34 +4,33 @@ import org.junit.Test; import static io.github.nblxa.cons.ConsList.*; -import static org.assertj.core.api.Assertions.assertThat; public class NilEqualsTest { @Test public void equalsHashCode_consListImpl() { - EqualsVerifier.forClass(ConsList.class) + EqualsVerifier.forClass(Nil.class) .withPrefabValues(ConsList.class, nil(), list("a", "b", "c")) .verify(); } @Test public void equalsHashCode_intConsListImpl() { - EqualsVerifier.forClass(ConsList.class) + EqualsVerifier.forClass(Nil.class) .withPrefabValues(ConsList.class, nil(), intList(1, 2, 3)) .verify(); } @Test public void equalsHashCode_longConsListImpl() { - EqualsVerifier.forClass(ConsList.class) + EqualsVerifier.forClass(Nil.class) .withPrefabValues(ConsList.class, nil(), longList(1L, 2L, 3L)) .verify(); } @Test public void equalsHashCode_doubleConsListImpl() { - EqualsVerifier.forClass(ConsList.class) + EqualsVerifier.forClass(Nil.class) .withPrefabValues(ConsList.class, nil(), doubleList(1.1d, 2.1d, 3.1d)) .verify(); } diff --git a/cons-list/src/test/java/io/github/nblxa/cons/NilTest.java b/cons-list/src/test/java/io/github/nblxa/cons/NilTest.java index b19a27f..8ccb393 100644 --- a/cons-list/src/test/java/io/github/nblxa/cons/NilTest.java +++ b/cons-list/src/test/java/io/github/nblxa/cons/NilTest.java @@ -163,55 +163,55 @@ public void testNilDoubleStream() { public void testReverse() { ConsList nil = nil(); assertThat(nil.reverse()) - .isSameAs(nil); + .isEqualTo(nil); } @Test public void testIntReverse() { IntConsList nil = nil(); assertThat(nil.intReverse()) - .isSameAs(nil); + .isEqualTo(nil); } @Test public void testLongReverse() { LongConsList nil = nil(); assertThat(nil.longReverse()) - .isSameAs(nil); + .isEqualTo(nil); } @Test public void testDoubleReverse() { DoubleConsList nil = nil(); assertThat(nil.doubleReverse()) - .isSameAs(nil); + .isEqualTo(nil); } @Test public void testMap() { ConsList nil = nil(); assertThat(nil.map(Object::toString)) - .isSameAs(nil); + .isEqualTo(nil); } @Test public void testIntMap() { IntConsList nil = nil(); assertThat(nil.intMap(i -> i + 1)) - .isSameAs(nil); + .isEqualTo(nil); } @Test public void testLongMap() { LongConsList nil = nil(); assertThat(nil.longMap(i -> i + 1L)) - .isSameAs(nil); + .isEqualTo(nil); } @Test public void testDoubleMap() { DoubleConsList nil = nil(); assertThat(nil.doubleMap(i -> i + 1d)) - .isSameAs(nil); + .isEqualTo(nil); } }