项目作者: anicolaspp

项目描述 :
A composable library for collections
高级语言: Java
项目地址: git://github.com/anicolaspp/nSource.git
创建时间: 2018-09-26T18:31:01Z
项目社区:https://github.com/anicolaspp/nSource

开源协议:MIT License

下载


Logo

Maven Central
Build Status

nSource

nSource is a small, stream-like, fully featured and functional library to chain computation stages on top of regular Java Collections. This library is intended as a case of study for lazy design and to demonstrate how laziness is an important part when building performant software.

  1. <dependency>
  2. <groupId>com.github.anicolaspp</groupId>
  3. <artifactId>nSource</artifactId>
  4. <version>1.0.1</version>
  5. </dependency>

Features

nSource has a few interesting characteristics.

  • it is lazy, until the last materialization stage.
  • its API is functional, so we can use declarative flow control.
  • it has a familiar API, by using stream-like naming.
  • It is optimized, so it operates on the minimum and only necessary elements of the underlying data source.

Usage

nSource allows us to do the following.

  1. RunnableStage<List<String>> s = nsource
  2. .from(getNumbers())
  3. .filter(x-> x % 2 == 0)
  4. .map(x-> x.toString())
  5. .toList();
  6. List<String> f = s.run();

Notice that nothing happens until we call .run() of the corresponding RunnableStage<>.

As we can see, this library can be seen in a similar way to what Java Streams offer and even though the intention is not to replace Streams, the library shows how the inception of lazy desing is a building blog when building smart components that need to present good performance.

Stream-like API

The main component of the nSource is ComposableStage<> and the followings are some of the present combinators.

  1. public <B> ComposableStage<B> map(Function<A, B> fn)
  2. public ComposableStage<A> filter(Predicate<A> predicate)
  3. public ComposableStage<A> take(int n)
  4. public ComposableStage<A> takeWhile(Predicate<A> predicate)
  5. public RunnableStage<List<A>> toList()
  6. public <B> RunnableStage<B> foldLeft(B zero, BiFunction<A, B, B> biFunction)
  7. public RunnableStage<Done> forEach(Consumer<A> consumer)
  8. public RunnableStage<Optional<A>> first()
  9. public RunnableStage<A> firstOrDefault(Supplier<A> defaultValue)

This is NOT the entire list

As we can appreciate, it shares a lot with Java Streams but it holds on laziness as much as it can.

nSource ComposableStage<> are meant to be used for chaining operations. However, once we get a RunnableStage<> we should not reuse the corresponding ComposableStage<>.

  1. String name = ....
  2. RunnableStage<Integer> sum = nSource
  3. .from(getValues(...))
  4. .filter(v -> v.name.equals(name))
  5. .map(v -> v.getAge())
  6. .foldLeft(0, (a, b) -> a + b);
  7. assert sum.run() == sum.run()

In here, the computations happens only once (the first time we call .run() and the second time the value is reused internally.

On the other hand, the following might cause a source reuse, and that is not allowed.

  1. ComposableStage<List<Integer>> stage = nSource
  2. .from(getValues(...))
  3. .filter(v -> v.name.equals(name))
  4. .map(v -> v.getAge());
  5. RunnableStage<Integer> sum = stage.foldLeft(0, (a, b) -> a + b);
  6. RunnableStage<Done> printThem = stage.forEach(System.out::println);

This is perfectly valid since nothing has been executed just yet, but then we should only be able to materialize exactly one of them (the first one to call .run().

  1. printThem.run();
  2. ...
  3. int sumValue = sum.run(); // MaterializationException thrown here.

The first one to be materialized wins.

This library is not intended to be used in production, but to present implementation details on lazy and smart design/implementation.