Java - Lambda

Updated: 2019-01-24

Stream

Create a Stream of String:

jshell> Stream<String> s = Stream.of("a", "b", "c")
s ==> java.util.stream.ReferencePipeline$Head@51565ec2

To collect the stream into a list:

jshell> s.collect(Collectors.toList())
$1 ==> [a, b, c]

Note that Stream can only be "streamed" once

jshell> s.collect(Collectors.toList())
|  Exception java.lang.IllegalStateException: stream has already been operated upon or closed
|        at AbstractPipeline.evaluate (AbstractPipeline.java:229)
|        at ReferencePipeline.collect (ReferencePipeline.java:578)
|        at (#11:1)

Lambda

Combining Stream and lambda function we can do a lot of interesting operations.

Change All Strings to Uppercase

jshell> Stream<String> s = Stream.of("a", "b", "c")
s ==> java.util.stream.ReferencePipeline$Head@51565ec2

jshell> s.map(c -> c.toUpperCase()).collect(Collectors.toList())
$1 ==> [A, B, C]

Filter

jshell> int[] a = new int[]{1, -2, 3, -4}
a ==> int[4] { 1, -2, 3, -4 }

jshell> Arrays.stream(a).filter(x -> x > 0).toArray()
$1 ==> int[2] { 1, 3 }

Convert Byte Array To Hex String

1 byte is 8-bit, so each byte can be represented by 2 hex characters, e.g. 127 => 7F, 1 => 01.

Arrays.stream() only supports double[], int[], long[] or object arrays, so it cannot be used on byte arrays(byte[]). Here's a workaround:

jshell> byte[] b = new byte[]{1, 127, -3, 42}
b ==> byte[4] { 1, 127, -3, 42 }

jshell> IntStream.range(0, b.length).mapToObj(i -> String.format("%02X", b[i])).collect(Collectors.joining(" "))
$1 ==> "01 7F FD 2A"

One-liner

Print all the lines:

Files.lines(Paths.get("./data.txt")).forEach(System.out::println);

Pattern matching:

Pattern.compile(" ").splitAsStream("a b c").forEach(System.out::println);

Generate random integers

new Random().ints().limit(5).forEach(System.out::println);

Print sorted chars

"hello".chars().sorted().forEach(ch -> System.out.printf("%c ", ch));

Word count

lines.stream()
    .flatMap(line -> Stream.of(line.split("\\W+")))
    .collect(Collectors.groupingBy(String::toLowerCase, Collectors.summingInt(s -> 1)));

Functional Interfaces

A Functional Interface has one and only one abstract method, and @FunctionalInterface annotation enforces that.

The following functional interfaces are defined in java.util.function:

  • Consumer: one input, no output(indicating there must be some side effect)

    • BiConsumer: two inputs, no output
  • Function: one input, one output

    • BiFunction: two inputs, one output
  • Supplier: no input, one output
  • Predicate: one input, true/false output

For example, we can define a Consumer(i.e. a function without output), and reuse it:

jshell> Consumer<Integer> print = i -> System.out.print(i)
print ==> $Lambda$48/0x00000008000dc440@614ddd49

jshell> IntStream.range(0, 5).boxed().forEach(print)
01234

jshell> IntStream.range(5, 10).boxed().forEach(print)
56789

Note that boxed() is used to convert IntStream to Stream<Integer>.