Showing posts with label streams. Show all posts
Showing posts with label streams. Show all posts

Saturday, August 14, 2021

Java 16: Stream.mapMulti

Java 16 introduces a new Stream.mapMulti method which allows you to replace elements in a stream with multiple elements.

The example below shows how you can use mapMulti to replace each string in a stream with its uppercased and lowercased versions:

Stream.of("Twix", "Snickers", "Mars")
  .mapMulti((s, c) -> {
    c.accept(s.toUpperCase());
    c.accept(s.toLowerCase());
  })
  .forEach(System.out::println);

Output:
TWIX
twix
SNICKERS
snickers
MARS
mars

The same thing can also be achieved using flatMap like this:

Stream.of("Twix", "Snickers", "Mars")
  .flatMap(s -> Stream.of(s.toUpperCase(), s.toLowerCase()))
  .forEach(System.out::println);

So what is the difference between mapMulti and flatMap? According to the javadocs:

This method is preferable to flatMap in the following circumstances:

  • When replacing each stream element with a small (possibly zero) number of elements. Using this method avoids the overhead of creating a new Stream instance for every group of result elements, as required by flatMap.
  • When it is easier to use an imperative approach for generating result elements than it is to return them in the form of a Stream.

Inspecting the code for multiMap, we can see that it delegates to flatMap, however, it makes use of a SpinedBuffer to hold the elements before creating the stream, thus avoiding the overhead of creating new streams per group of result elements.

default <R> Stream<R> mapMulti(BiConsumer<? super T, ? super Consumer<R>> mapper) {
  Objects.requireNonNull(mapper);
  return flatMap(e -> {
    SpinedBuffer<R> buffer = new SpinedBuffer<>();
    mapper.accept(e, buffer);
    return StreamSupport.stream(buffer.spliterator(), false);
  });
}

Saturday, April 10, 2021

Java 16: Stream.toList()

Java 16 introduces a handy new Stream.toList() method which makes it easier to convert a stream into a list. The returned list is unmodifiable and calls to any mutator method will throw an UnsupportedOperationException.

Here is some sample code:

import java.util.stream.Stream;
import static java.util.stream.Collectors.*;

// Java 16
stream.toList(); // returns an unmodifiable list

// Other ways to create Lists from Streams:

stream.collect(toList());

stream.collect(toCollection(LinkedList::new)); // if you need a specific type of list

stream.collect(toUnmodifiableList());  // introduced in Java 10

stream.collect(
    collectingAndThen(toList(), Collections::unmodifiableList)); // pre-Java 10

Related post: Java 10: Collecting a Stream into an Unmodifiable Collection

Saturday, December 22, 2018

Java: Streaming a JDBC ResultSet as CSV

In my previous post, I showed how to convert a java.sql.ResultSet to JSON and stream it back to the caller. This post, is about streaming it in CSV format instead. Streaming allows you to transfer the data, little by little, without having to load it all into the server's memory.

For example, consider the following ResultSet:

+---------+-----+
| Name    | Age |
+---------+-----+
| Alice   |  20 |
| Bob     |  35 |
| Charles |  50 |
+---------+-----+

The corresponding CSV is:

name,age
Alice,20
Bob,35
Charles,50

The following class (also available in my GitHub Repository) can be used to convert the ResultSet to CSV. Note that this class implements Spring's ResultSetExtractor, which can be used by a JdbcTemplate to extract results from a ResultSet.

/**
 * Streams a ResultSet as CSV.
 */
public class StreamingCsvResultSetExtractor
                         implements ResultSetExtractor<Void> {

  private static char DELIMITER = ',';

  private final OutputStream os;

  /**
   * @param os the OutputStream to stream the CSV to
   */
  public StreamingCsvResultSetExtractor(final OutputStream os) {
    this.os = os;
  }

  @Override
  public Void extractData(final ResultSet rs) {
    try (var pw = new PrintWriter(os, true)) {
      final var rsmd = rs.getMetaData();
      final var columnCount = rsmd.getColumnCount();
      writeHeader(rsmd, columnCount, pw);
      while (rs.next()) {
        for (var i = 1; i <= columnCount; i++) {
          final var value = rs.getObject(i);
          pw.write(value == null ? "" : value.toString());
          if (i != columnCount) {
            pw.append(DELIMITER);
          }
        }
        pw.println();
      }
      pw.flush();
    } catch (final SQLException e) {
      throw new RuntimeException(e);
    }
    return null;
  }

  private static void writeHeader(final ResultSetMetaData rsmd,
      final int columnCount, final PrintWriter pw) throws SQLException {
    for (var i = 1; i <= columnCount; i++) {
      pw.write(rsmd.getColumnName(i));
      if (i != columnCount) {
        pw.append(DELIMITER);
      }
    }
    pw.println();
  }
}

To use this in a web service with JAX-RS:

import javax.ws.rs.core.StreamingOutput;

@GET
@Path("runQuery")
@Produces("text/csv")
public StreamingOutput runQuery() {
  return new StreamingOutput() {
    @Override
    public void write(final OutputStream os)
        throws IOException, WebApplicationException {
      jdbcTemplate.query("select name, age from person",
                   new StreamingCsvResultSetExtractor(os));
    }
  };
}
Related posts:
Streaming a JDBC ResultSet as JSON

Saturday, September 15, 2018

Java: Streaming a JDBC ResultSet as JSON

This post shows how you can convert a java.sql.ResultSet to JSON and stream it back to the caller. This is useful if you want to send a large dataset from a JDBC DataSource to a web application in JSON format. Streaming allows you to transfer the data, little by little, without having to load it all into the server's memory.

For example, consider the following ResultSet:

+---------+-----+
| name    | age |
+---------+-----+
| Alice   |  20 |
| Bob     |  35 |
| Charles |  50 |
+---------+-----+

The corresponding JSON is:

[
  { "name": "Alice",   "age": 20 },
  { "name": "Bob",     "age": 35 },
  { "name": "Charles", "age": 50 },
]

The following class (also available in my GitHub Repository) can be used to convert the ResultSet to JSON. Note that this class implements Spring's ResultSetExtractor, which can be used by a JdbcTemplate to extract results from a ResultSet.

/**
 * Streams a ResultSet as JSON.
 */
public class StreamingJsonResultSetExtractor implements ResultSetExtractor<Void> {

  private final OutputStream os;

  /**
   * @param os the OutputStream to stream the JSON to
   */
  public StreamingJsonResultSetExtractor(final OutputStream os) {
    this.os = os;
  }

  @Override
  public Void extractData(final ResultSet rs) {
    final var objectMapper = new ObjectMapper();
    try (var jg = objectMapper.getFactory().createGenerator(
                  os, JsonEncoding.UTF8)) {
      writeResultSetToJson(rs, jg);
      jg.flush();
    } catch (IOException | SQLException e) {
      throw new RuntimeException(e);
    }
    return null;
  }

  private static void writeResultSetToJson(final ResultSet rs,
                            final JsonGenerator jg)
                            throws SQLException, IOException {
    final var rsmd = rs.getMetaData();
    final var columnCount = rsmd.getColumnCount();
    jg.writeStartArray();
    while (rs.next()) {
      jg.writeStartObject();
      for (var i = 1; i <= columnCount; i++) {
        jg.writeObjectField(rsmd.getColumnName(i), rs.getObject(i));
      }
      jg.writeEndObject();
    }
    jg.writeEndArray();
  }
}

To use this in a web service with JAX-RS:

import javax.ws.rs.core.StreamingOutput;

@GET
@Path("runQuery")
public StreamingOutput runQuery() {
  return new StreamingOutput() {
    @Override
    public void write(final OutputStream os)
        throws IOException, WebApplicationException {
      jdbcTemplate.query("select name, age from person",
                   new StreamingJsonResultSetExtractor(os));
    }
  };
}

Saturday, June 02, 2018

Java 10: Collecting a Stream into an Unmodifiable Collection

Java 10 introduces several new methods to facilitate the creation of unmodifiable collections.

The List.copyOf, Set.copyOf, and Map.copyOf methods create new collection instances from existing instances. For example:

List<String> modifiable = Arrays.asList("foo", "bar");
List<String> unmodifiableCopy = List.copyOf(list);

// Note that since Java 9, you can also use "of" to create
// unmodifiable collections
List<String> unmodifiable = List.of("foo", "bar");

There are also new collector methods, toUnmodifiableList, toUnmodifiableSet, and toUnmodifiableMap, to allow the elements of a stream to be collected into an unmodifiable collection. For example:


// Java 10
Stream.of("foo", "bar").collect(toUnmodifiableList());

// before Java 10
Stream.of("foo", "bar").collect(
    collectingAndThen(toList(), Collections::unmodifiableList));

Monday, December 25, 2017

Java 9: Enhancements to the Stream API

Java 9 adds 4 new methods to the Stream interface:

1. dropWhile

The dropWhile method is similar to the skip method but uses a Predicate instead of a fixed integer value. It drops elements from the input stream while the Predicate is true. All remaining elements are then passed to the output stream. For example:

IntStream.range(0, 10)
         .dropWhile(i -> i < 5)
         .forEach(System.out::println);
// prints 5, 6, 7, 8, 9

2. takeWhile

The takeWhile method is similar to the limit method. It takes elements from the input stream and passes them to the output stream while the Predicate is true. For example:

IntStream.range(0, 10)
         .takeWhile(i -> i < 5)
         .forEach(System.out::println);
// prints 0, 1, 2, 3, 4
Note: Be careful when using dropWhile and takeWhile when you have an unordered stream because you might get elements in the output stream that you don't expect.

3. ofNullable

The ofNullable method returns an empty stream if the element is null, or a single-element stream if non-null. This eliminates the need for a null check before constructing a stream.

Stream.ofNullable(null).count();  // prints 0
Stream.ofNullable("foo").count(); // prints 1

4. iterate

The static iterate method has been overloaded in Java 9 to allow you to create a stream using the for-loop syntax. For example:

Stream.iterate(0, i -> i < 10, i -> i + 1)
      .forEach(System.out::println); //prints from 0 to 9

Saturday, March 11, 2017

Java 8: Tree Traversal Using Streams

It's quite common to traverse a tree using recursion, but with Java 8 it is now possible to lazily traverse a tree using streams.

The class below represents a tree. The stream() method streams the nodes contained in the tree. You can then do all the cool things that you can do with other streams, such as filtering, mapping and collecting!

public class TreeNode<E> {

  private final E data;
  private final List<TreeNode<E>> children;

  /**
   * Creates the tree node with the specified data and no children.
   * @param data
   */
  public TreeNode(final E data) {
    this.data = data;
    this.children = new ArrayList<>();
  }

  /**
   * @return the data contained in this node
   */
  public E getData() {
    return data;
  }

  /**
   * Adds a child to this tree node.
   * @param data the data to add
   */
  public TreeNode<E> addChild(final E data) {
    final TreeNode<E> toAdd = new TreeNode<>(data);
    children.add(toAdd);
    return toAdd;
  }

  /**
   * @return a stream of nodes in this tree in depth first order
   */
  public Stream<TreeNode<E>> stream() {
    return Stream.concat(Stream.of(this), children.stream().flatMap(TreeNode::stream));
  }
}

Example usage:

TreeNode<String> root = new TreeNode<>("Root");
TreeNode<String> a = root.addChild("A");
a.addChild("A1");
a.addChild("A2");
root.addChild("B");
TreeNode<String> c = root.addChild("C");
c.addChild("C1");

int count = root.stream().count(); // 7

String tree = root.stream().map(TreeNode::getData).collect(Collectors.joining(","));
// Root,A,A1,A2,B,C,C1

Sunday, January 22, 2017

Java 8: Top N elements from a stream

This post shows how you can sort a stream in reverse order and then select the top N elements.

This is quite common in financial systems.

For example, let's say that you have a list of currency exchange rate movements and you want to see the top 5 largest movements. You can do this using Java 8 Streams as shown below:

import static java.util.Comparator.*;
import static java.util.stream.Collectors.*;

// list of currency exchange rate percentage moves
final List<Currency> currencies = Arrays.asList(
    new Currency("EUR/USD", 0.37),
    new Currency("USD/JPY", -0.21),
    new Currency("GBP/USD", 0.27),
    new Currency("AUD/USD", -0.08),
    new Currency("USD/CAD", 0.02),
    new Currency("USD/CHF", -0.46),
    new Currency("EUR/JPY", 0.16),
    new Currency("EUR/GBP", 0.13),
    new Currency("USD/HKD", 0.0),
    new Currency("EUR/CHF", 0.05),
    new Currency("USD/KRW", -0.71)
    );

currencies.stream()
  .sorted(comparing(Currency::getMove, comparing(Math::abs)).reversed())
  .limit(5)
  .collect(toList());

The result is:

Currency [ccy=USD/KRW, move=-0.71]
Currency [ccy=USD/CHF, move=-0.46]
Currency [ccy=EUR/USD, move=0.37]
Currency [ccy=GBP/USD, move=0.27]
Currency [ccy=USD/JPY, move=-0.21]

The two argument Comparator.comparing method easily allows us to compare currency moves on absolute value.

Sunday, September 11, 2016

Factorial Using Iteration, Recursion and Java 8 Streams

In this post, we will solve the classic school problem of calculating the factorial of a positive integer, using different Java algorithms.

Note: In all the algorithms below, we assume that the input is > 1.

1. Iterative Factorial

In this algorithm, we use a standard for-loop and increment the variables i and result at each iteration:

static int iterativeFactorial(final int n) {
  int result = 1;
  for (int i = 1; i <= n; i++) {
    result *= i;
  }
  return result;
}
2. Recursive Factorial

Here is the recursive version (the function calls itself):

static int recursiveFactorial(final int n) {
  return n == 1 ? 1 : n * recursiveFactorial(n - 1);
}

Recursion lets you get rid of variables that are updated at each iteration. Typically, making a recursive function call is more expensive than iteration because each time the function is called, a new stack frame has to be created on the call stack to hold the state of each function call. Therefore, the memory consumed by the recursive function is proportional to the size of its input. You may even get a StackOverflowError with a large input.

3. Tail-recursive Factorial

Here's a tail-recursive definition of factorial. The recursive call is the last thing that happens in the function. In contrast, in our previous definition of factorial, the last thing was n multiplied by the result of the recursive call.

static int tailRecursiveFactorial(final int n) {
  return tailRecursiveFactorialHelper(n, 1);
}
private static int tailRecursiveFactorialHelper(final int n, final int acc) {
  return n == 1 ? acc : tailRecursiveFactorialHelper(n - 1, n * acc);
}

Tail-recursive functions can be optimised by the compiler in some programming languages. Instead of storing each intermediate result of the recursion onto different stack frames, a single stack frame can be reused instead, because there's no need to keep track of intermediate results. Unfortunately, Java does not support this kind of optimisation, but you might want to take this approach anyway, in case the compiler is optimised in the future. Other JVM languages (e.g. Scala) can optimise tail-recursion.

4. Stream Factorial

Finally, Java 8 streams provide a simple, declarative way of defining factorial:

static int streamFactorial(final int n) {
  return IntStream.rangeClosed(1, n).reduce(1, (a, b) -> a * b);
}

Sunday, June 26, 2016

Java 8: CompletableFuture vs Parallel Stream

This post shows how Java 8's CompletableFuture compares with parallel streams when peforming asynchronous computations.

We will use the following class to model a long-running task:

class MyTask {
  private final int duration;
  public MyTask(int duration) {
    this.duration = duration;
  }
  public int calculate() {
    System.out.println(Thread.currentThread().getName());
    try {
      Thread.sleep(duration * 1000);
    } catch (final InterruptedException e) {
      throw new RuntimeException(e);
    }
    return duration;
  }
}

Let's create ten tasks, each with a duration of 1 second:

List<MyTask> tasks = IntStream.range(0, 10)
                                    .mapToObj(i -> new MyTask(1))
                                    .collect(toList());

How can we calculate the list of tasks efficiently?

Approach 1: Sequentially

Your first thought might be to calculate the tasks sequentially, as follows:

public static void runSequentially(List<MyTask> tasks) {
  long start = System.nanoTime();
  List<Integer> result = tasks.stream()
                              .map(MyTask::calculate)
                              .collect(toList());
  long duration = (System.nanoTime() - start) / 1_000_000;
  System.out.printf("Processed %d tasks in %d millis\n", tasks.size(), duration);
  System.out.println(result);
}

As you might expect, this takes 10 seconds to run, because each task is run one after the other on the main thread.

Approach 2: Using a parallel stream

A quick improvement is to convert your code to use a parallel stream, as shown below:

public static void useParallelStream(List<MyTask> tasks) {
  long start = System.nanoTime();
  List<Integer> result = tasks.parallelStream()
                              .map(MyTask::calculate)
                              .collect(toList());
  long duration = (System.nanoTime() - start) / 1_000_000;
  System.out.printf("Processed %d tasks in %d millis\n", tasks.size(), duration);
  System.out.println(result);
}

The output is

main
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-2
main
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-1
main
Processed 10 tasks in 3043 millis

This time it took 3 seconds because 4 tasks were run in parallel (using three threads from the ForkJoinPool, plus the main thread).

Approach 3: Using CompletableFutures

Let's see if CompletableFutures perform any better:

public static void useCompletableFuture(List<MyTask> tasks) {
  long start = System.nanoTime();
  List<CompletableFuture<Integer>> futures =
      tasks.stream()
           .map(t -> CompletableFuture.supplyAsync(() -> t.calculate()))
           .collect(Collectors.toList());

  List<Integer> result =
      futures.stream()
             .map(CompletableFuture::join)
             .collect(Collectors.toList());
  long duration = (System.nanoTime() - start) / 1_000_000;
  System.out.printf("Processed %d tasks in %d millis\n", tasks.size(), duration);
  System.out.println(result);
}

In the code above, we first obtain a list of CompletableFutures and then invoke the join method on each future to wait for them to complete one by one. Note that join is the same as get, with the only difference being that the former doesn't throw any checked exception, so it's more convenient in a lambda expression.

Also, you must use two separate stream pipelines, as opposed to putting the two map operations after each other, because intermediate stream operations are lazy and you would have ended up processing your tasks sequentially! That's why you first need to collect your CompletableFutures in a list to allow them to start before waiting for their completion.

The output is

ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-1
ForkJoinPool.commonPool-worker-2
ForkJoinPool.commonPool-worker-3
ForkJoinPool.commonPool-worker-1
Processed 10 tasks in 4010 millis

It took 4 seconds to process 10 tasks. You will notice that only 3 ForkJoinPool threads were used and that, unlike the parallel stream, the main thread was not used.

Approach 4: Using CompletableFutures with a custom Executor

One of the advantages of CompletableFutures over parallel streams is that they allow you to specify a different Executor to submit their tasks to. This means that you can choose a more suitable number of threads based on your application. Since my example is not very CPU-intensive, I can choose to increase the number of threads to be greater than Runtime.getRuntime().getAvailableProcessors(), as shown below:

public static void useCompletableFutureWithExecutor(List<MyTask> tasks) {
  long start = System.nanoTime();
  ExecutorService executor = Executors.newFixedThreadPool(Math.min(tasks.size(), 10));
  List<CompletableFuture<Integer>> futures =
      tasks.stream()
           .map(t -> CompletableFuture.supplyAsync(() -> t.calculate(), executor))
           .collect(Collectors.toList());

  List<Integer> result =
      futures.stream()
             .map(CompletableFuture::join)
             .collect(Collectors.toList());
  long duration = (System.nanoTime() - start) / 1_000_000;
  System.out.printf("Processed %d tasks in %d millis\n", tasks.size(), duration);
  System.out.println(result);
  executor.shutdown();
}

The output is

pool-1-thread-2
pool-1-thread-4
pool-1-thread-3
pool-1-thread-1
pool-1-thread-5
pool-1-thread-6
pool-1-thread-7
pool-1-thread-8
pool-1-thread-9
pool-1-thread-10
Processed 10 tasks in 1009 millis

After this improvement, it now takes only 1 second to process 10 tasks.

As you can see, CompletableFutures provide more control over the size of the thread pool and should be used if your tasks involve I/O. However, if you're doing CPU-intensive operations, there's no point in having more threads than processors, so go for a parallel stream, as it is easier to use.

Saturday, June 18, 2016

Java 8: Debugging Stream Pipelines

I've found that stream pipelines can be difficult to debug because stack traces involving lambda expressions are quite cryptic. Consider the following contrived example:

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class Test {
  public static void main(final String[] args) {
    final List<String> list = Arrays.asList("foo", null, "bar");
    list.stream()
        .map(Function.identity())
        .filter(x -> true)
        .map(String::length)
        .forEach(System.out::println);
  }
}

You may have already guessed that the code above will throw a NullPointerException when String.length is called on the null element in the list. I've added extra map and filter operations, which do nothing, just to make the example a bit more interesting. In the real world, you will probably have a number of different operations in your stream pipeline.

Running the code, produces the following stack trace:

Exception in thread "main" java.lang.NullPointerException
  at Test$$Lambda$3/455659002.apply(Unknown Source)
  at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
  at java.util.stream.ReferencePipeline$2$1.accept(Unknown Source)
  at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
  at java.util.Spliterators$ArraySpliterator.forEachRemaining(Unknown Source)
  at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
  at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
  at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
  at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
  at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
  at java.util.stream.ReferencePipeline.forEach(Unknown Source)
  at Test.main(Test.java:12)

The stack trace shows that a NullPointerException occurred but it doesn't tell you which operation in the pipeline failed. What does Test$$Lambda$3/455659002.apply(Unknown Source) mean and why is there no line number?! Since lambda expressions don't have a name, the compiler makes one up (similar to anonymous classes). In this case, it is Test$$Lambda$3 but that doesn't help us track the bug in our code.

So, what can we do? Let's go old-school and add some logging to our code! We can use peek to print out each element before it is consumed by the next operation in the pipeline.

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class Test {
  public static void main(final String[] args) {
    final List<String> list = Arrays.asList("foo", null, "bar");
    list.stream()
        .peek(x -> System.out.println("Running identity on: " + x))
        .map(Function.identity())
        .peek(x -> System.out.println("Running filter on: " + x))
        .filter(x -> true)
        .peek(x -> System.out.println("Running string length on: " + x))
        .map(String::length)
        .peek(x -> System.out.println("Running print on: " + x))
        .forEach(System.out::println);
  }
}

Running it produces the following output:

Running identity map on: foo
Running filter on: foo
Running string length on: foo
Running print on: 3
3
Running identity map on: null
Running filter on: null
Running string length on: null
Exception in thread "main" java.lang.NullPointerException
  at Test$$Lambda$6/295530567.apply(Unknown Source)
  at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
  at java.util.stream.ReferencePipeline$11$1.accept(Unknown Source)
  at java.util.stream.ReferencePipeline$2$1.accept(Unknown Source)
  at java.util.stream.ReferencePipeline$11$1.accept(Unknown Source)
  at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
  at java.util.stream.ReferencePipeline$11$1.accept(Unknown Source)
  at java.util.Spliterators$ArraySpliterator.forEachRemaining(Unknown Source)
  at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
  at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
  at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
  at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
  at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
  at java.util.stream.ReferencePipeline.forEach(Unknown Source)
  at Test.main(Test.java:16)

Great! Now we know that the NullPointerException was thrown by the string length lambda!

In general, I think stack traces involving lambdas could be improved in future versions of Java.

Sunday, April 17, 2016

Java 8 Streams API: Immutable List Collector

Instead of first collecting a stream into a list and then making it unmodifiable, as shown below:

mutableList = list.stream()
                  // perform some stream operations...
                  .filter(myFilterPredicate)
                  .map(myMapperFunction)
                  // collect into a list
                  .collect(toList());

// now make the list unmodifiable
return Collections.unmodifiableList(list);

you can using collectingAndThen as follows:

return list.stream()
           // perform some stream operations...
           .filter(myFilterPredicate)
           .map(myMapperFunction)
           // collect into an unmodifiable list
           .collect(collectingAndThen(toList(), Collections::unmodifiableList));

Sunday, March 20, 2016

Java 8: Joining a Stream

In a previous post, I wrote about how you can use Guava's Joiner to easily convert an Iterable into a String, so you no longer have to iterate over it and build the String manually.

Java 8 introduces the StringJoiner, which can be used to join a Stream using Collectors.joining. For example:

Arrays.asList("apple", "banana", "cherry")
      .stream()
      .collect(Collectors.joining(", ");

To skip nulls and empty strings, you can filter the stream first:

Arrays.asList("apple", "banana", null, "", "cherry")
      .stream()
      .filter(s -> s != null && !s.isEmpty())
      .collect(Collectors.joining(", ");

I'd still use Guava's Joiner for lists though.

Monday, December 28, 2015

Java 8: Convert a String to a Stream of Characters

I find it a bit strange that the Java API does not provide a method to convert a String to a Stream<Character>, but this is how you can do it:

Stream<Character> stream = string.chars().mapToObj(i -> (char)i);

// or:

Stream<Character> stream = IntStream.range(0, string.length())
                                    .mapToObj(string::charAt);

Sunday, November 29, 2015

Java 8 Streams API: Grouping and Partitioning a Stream

This post shows how you can use the Collectors available in the Streams API to group elements of a stream with groupingBy and partition elements of a stream with partitioningBy.

Consider a stream of Employee objects, each with a name, city and number of sales, as shown in the table below:

+----------+------------+-----------------+
| Name     | City       | Number of Sales |
+----------+------------+-----------------+
| Alice    | London     | 200             |
| Bob      | London     | 150             |
| Charles  | New York   | 160             |
| Dorothy  | Hong Kong  | 190             |
+----------+------------+-----------------+

Grouping

Let's start by grouping employees by city using imperative style (pre-lamba) Java:

Map<String, List<Employee>> result = new HashMap<>();
for (Employee e : employees) {
  String city = e.getCity();
  List<Employee> empsInCity = result.get(city);
  if (empsInCity == null) {
    empsInCity = new ArrayList<>();
    result.put(city, empsInCity);
  }
  empsInCity.add(e);
}

You're probably familiar with writing code like this, and as you can see, it's a lot of code for such a simple task!

In Java 8, you can do the same thing with a single statement using a groupingBy collector, like this:

Map<String, List<Employee>> employeesByCity =
  employees.stream().collect(groupingBy(Employee::getCity));

This results in the following map:

{New York=[Charles], Hong Kong=[Dorothy], London=[Alice, Bob]}

It's also possible to count the number of employees in each city, by passing a counting collector to the groupingBy collector. The second collector performs a further reduction operation on all the elements in the stream classified into the same group.

Map<String, Long> numEmployeesByCity =
  employees.stream().collect(groupingBy(Employee::getCity, counting()));

The result is the following map:

{New York=1, Hong Kong=1, London=2}

Just as an aside, this is equivalent to the following SQL statement:

select city, count(*) from Employee group by city

Another example is calculating the average number of sales in each city, which can be done using the averagingInt collector in conjuction with the groupingBy collector:

Map<String, Double> avgSalesByCity =
  employees.stream().collect(groupingBy(Employee::getCity,
                               averagingInt(Employee::getNumSales)));

The result is the following map:

{New York=160.0, Hong Kong=190.0, London=175.0}

Partitioning

Partitioning is a special kind of grouping, in which the resultant map contains at most two different groups - one for true and one for false. For instance, if you want to find out who your best employees are, you can partition them into those who made more than N sales and those who didn't, using the partitioningBy collector:

Map<Boolean, List<Employee>> partitioned =
  employees.stream().collect(partitioningBy(e -> e.getNumSales() > 150));

This will produce the following result:

{false=[Bob], true=[Alice, Charles, Dorothy]}

You can also combine partitioning and grouping by passing a groupingBy collector to the partitioningBy collector. For example, you could count the number of employees in each city within each partition:

Map<Boolean, Map<String, Long>> result =
  employees.stream().collect(partitioningBy(e -> e.getNumSales() > 150,
                               groupingBy(Employee::getCity, counting())));

This will produce a two-level Map:

{false={London=1}, true={New York=1, Hong Kong=1, London=1}}

Saturday, October 31, 2015

Java 8 Streams API: Finding and matching

The Streams API provides some useful methods to determine whether elements in a stream match a given condition.

anyMatch
The anyMatch method can be used to check if there exists an element in the stream that matches a given predicate. For example, to find out whether a stream of random numbers has a number greater than 5:

IntStream randomStream = new Random(100, 1, 11);
if (randomStream.anyMatch(i -> i > 5)) {
 System.out.println("The stream has a number greater than 5");
}

allMatch
The allMatch method can be used to check if all elements in the stream match a given predicate. For example, to find out whether a stream of random numbers only contains positive numbers:

boolean isPositive = randomStream.allMatch(i -> i > 0);

noneMatch
noneMatch is the opposite of allMatch and can be used to check that no elements in the stream match a given predicate. The previous example could be rewritten using noneMatch as follows:

boolean isPositive = randomStream.noneMatch(i -> i <= 0);

findAny
The findAny method returns an arbitrary element of the stream. It returns an Optional because it's possible that no element might be returned by findAny. For example, to find a number greater than 5 in our random number stream:

OptionalInt number = randomStream.filter(i -> i > 5)
                                 .findAny();

findFirst
findFirst is similar to findAny but returns the first element in the stream. For example, to find the first number greater than 5 in our random number stream:

OptionalInt number = randomStream.filter(i -> i > 5)
                                 .findFirst();

The difference between findAny and findFirst arises when using parallel streams. Finding an arbitrary element in a stream is less constraining than finding the first element, when running in parallel mode, so findAny may perform better. So, if you don't care about which element is returned, use findAny.

An interesting thing to note is that the operations described above use short-circuiting i.e. they don't need to process the entire stream to produce a result. As soon as an appropriate element is found, a result is returned.

Sunday, July 26, 2015

Java 8: Creating infinite streams

There are many ways you can build a Stream in Java 8. One of the most common ways, is to get a stream from a collection using the stream method as shown below:

List<String> list = Arrays.asList("Alice", "Bob");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);

You can also create a stream from values using the static method, Stream.of:

Stream<String> stream = Stream.of("Alice", "Bob");

Arrays can be converted to streams using the static method, Arrays.stream:

int[] numbers = { 1, 3, 6, 8 };
IntStream stream = Arrays.stream(numbers);
Creating infinite streams:

The Streams API provides two static methods: Stream.iterate and Stream.generate, which allow you to create an infinite stream. Here's an example:

Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2);

The example above produces an infinite stream of even numbers. The iterate method takes a starting value and a lambda that is used to generate a new value from the previous one. In this case, the lambda returns the previous value added with 2. The stream is infinite because values are computed on demand and can be computed forever. (Note that you can use the limit method to explicitly limit the size of the stream.)

Similarly, Stream.generate also lets you produce an infinite stream, but the difference is that it takes a lamba of type Supplier<T> to provide new values, rather than a lambda that applies successively to the previous value. Here's an example:

Stream<Integer> randomNumbers = Stream.generate(Math::random);