Skip to content
Open
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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -113,17 +113,17 @@ ConsList<String> 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).

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:

Expand Down
18 changes: 16 additions & 2 deletions cons-list/src/main/java/io/github/nblxa/cons/ConsList.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -221,7 +222,7 @@ static <V> ConsList<V> consList(@NonNull Iterable<V> iterable) {
@NonNull
@SafeVarargs
static <V> ConsList<V> concat(@NonNull ConsList<V> first, @NonNull ConsList<V>... 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;
Expand Down Expand Up @@ -616,7 +617,7 @@ static DoubleConsList<Double> doubleConsList(@NonNull Iterable<Double> iterable)
@NonNull
@SafeVarargs
static DoubleConsList<Double> concat(@NonNull DoubleConsList<Double> first, @NonNull DoubleConsList<Double>... 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;
Expand Down Expand Up @@ -669,4 +670,17 @@ static DoubleConsList<Double> concat(@NonNull DoubleConsList<Double> first, @Non
*/
@NonNull
ConsList<E> reverse();

/**
* Eager implementation of the <tt>map</tt> method that applies <tt>mapper</tt>
* to all elements of the <tt>ConsList</tt>.
*
* The resulting new list has the same order of elements.
*
* @param mapper the mapping function to be applied to all elements
* @param <T> new list&amp;s element type
* @return new resulting list
*/
@NonNull
<T> ConsList<T> map(@NonNull Function<? super E, ? extends T> mapper);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.*;
import java.util.function.Function;

@Immutable
@ThreadSafe
Expand Down Expand Up @@ -46,6 +47,12 @@ public ConsList<E> reverse() {
return result;
}

@NonNull
@Override
public <T> ConsList<T> map(@NonNull Function<? super E, ? extends T> mapper) {
return ConsUtil.map(this, mapper);
}

@Override
public boolean isEmpty() {
return false;
Expand Down
13 changes: 13 additions & 0 deletions cons-list/src/main/java/io/github/nblxa/cons/ConsUtil.java
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down Expand Up @@ -60,4 +63,14 @@ static <V, U> boolean haveEqualElements(DoubleConsList<V> first, DoubleConsList<
}
return first == Nil.INSTANCE && second == Nil.INSTANCE;
}

@NonNull
static <E, T> ConsList<T> map(@NonNull ConsList<E> cons, @NonNull Function<? super E, ? extends T> mappingFunction) {
ConsList<T> result = ConsList.nil();
while (cons != Nil.INSTANCE) {
result = new ConsListImpl<>(mappingFunction.apply(cons.head()), result);
cons = cons.tail();
}
return result.reverse();
}
}
13 changes: 13 additions & 0 deletions cons-list/src/main/java/io/github/nblxa/cons/DoubleConsList.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -63,4 +64,16 @@ public interface DoubleConsList<E> extends ConsList<E> {
*/
@NonNull
DoubleStream doubleStream();

/**
* Eager implementation of the <tt>map</tt> method that applies <tt>mapper</tt>
* to all elements of the <tt>DoubleConsList</tt>.
*
* 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<E> doubleMap(@NonNull DoubleUnaryOperator mapper);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
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;

Expand Down Expand Up @@ -49,6 +51,18 @@ public DoubleConsList<Double> doubleReverse() {
return result;
}

@NonNull
@Override
public DoubleConsList<Double> doubleMap(@NonNull DoubleUnaryOperator mapper) {
DoubleConsList<Double> result = ConsList.nil();
DoubleConsList<Double> cons = this;
while (cons != Nil.INSTANCE) {
result = new DoubleConsListImpl(mapper.applyAsDouble(cons.doubleHead()), result);
cons = cons.doubleTail();
}
return result.doubleReverse();
}

@NonNull
@Override
public Double head() {
Expand All @@ -67,6 +81,12 @@ public ConsList<Double> reverse() {
return doubleReverse();
}

@NonNull
@Override
public <T> ConsList<T> map(@NonNull Function<? super Double, ? extends T> mapper) {
return ConsUtil.map(this, mapper);
}

@Override
public boolean isEmpty() {
return false;
Expand Down
13 changes: 13 additions & 0 deletions cons-list/src/main/java/io/github/nblxa/cons/IntConsList.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -63,4 +64,16 @@ public interface IntConsList<E> extends ConsList<E> {
*/
@NonNull
IntStream intStream();

/**
* Eager implementation of the <tt>map</tt> method that applies <tt>mapper</tt>
* to all elements of the <tt>IntConsList</tt>.
*
* 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<E> intMap(@NonNull IntUnaryOperator mapper);
}
20 changes: 20 additions & 0 deletions cons-list/src/main/java/io/github/nblxa/cons/IntConsListImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.*;
import java.util.function.Function;
import java.util.function.IntUnaryOperator;
import java.util.stream.IntStream;
import java.util.stream.StreamSupport;

Expand Down Expand Up @@ -49,6 +51,18 @@ public IntConsList<Integer> intReverse() {
return result;
}

@NonNull
@Override
public IntConsList<Integer> intMap(@NonNull IntUnaryOperator mapper) {
IntConsList<Integer> result = ConsList.nil();
IntConsList<Integer> 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() {
Expand All @@ -67,6 +81,12 @@ public ConsList<Integer> reverse() {
return intReverse();
}

@NonNull
@Override
public <T> ConsList<T> map(@NonNull Function<? super Integer, ? extends T> mapper) {
return ConsUtil.map(this, mapper);
}

@Override
public boolean isEmpty() {
return false;
Expand Down
13 changes: 13 additions & 0 deletions cons-list/src/main/java/io/github/nblxa/cons/LongConsList.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -63,4 +64,16 @@ public interface LongConsList<E> extends ConsList<E> {
*/
@NonNull
LongStream longStream();

/**
* Eager implementation of the <tt>map</tt> method that applies <tt>mapper</tt>
* to all elements of the <tt>LongConsList</tt>.
*
* 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<E> longMap(@NonNull LongUnaryOperator mapper);
}
56 changes: 38 additions & 18 deletions cons-list/src/main/java/io/github/nblxa/cons/LongConsListImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import java.io.ObjectOutputStream;
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;

Expand Down Expand Up @@ -49,6 +51,36 @@ public LongConsList<Long> 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<Long> longMap(@NonNull LongUnaryOperator mapper) {
LongConsList<Long> result = ConsList.nil();
LongConsList<Long> 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() {
Expand All @@ -67,6 +99,12 @@ public ConsList<Long> reverse() {
return longReverse();
}

@NonNull
@Override
public <T> ConsList<T> map(@NonNull Function<? super Long, ? extends T> mapper) {
return ConsUtil.map(this, mapper);
}

@Override
public boolean isEmpty() {
return false;
Expand Down Expand Up @@ -95,24 +133,6 @@ public Spliterator<Long> 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) {
Expand Down
Loading