After migrating a three year old C# project to Java we ending up with a significant portion of legacy code using lambdas in Java. What was some of the good use cases, code which could be written better and the problems we had migrating from C#. At the end we look at the performance implications of using Lambdas.
2. Agenda
• Lambdas: A complicated way to do something simple?
• Lambda Patterns we used.
• Lambda Patterns we shouldn’t have used.
• Lambda Patterns we have used since.
4. Counting elements in a Stream
long count = list.stream().count();
// Stream.count()
@Override
public final long count() {
return mapToLong(e -> 1L).sum();
}
5. Counting elements in a Stream
long count = list.stream().count();
// LongStream.sum()
@Override
public final long sum() {
// use better algorithm to compensate for
intermediate overflow?
return reduce(0, Long::sum);
}
6. Counting elements in a Stream
long count = list.parallelStream().count();
When the examples get more complex, lambdas become much
more interesting.
7. Porting a legacy C# application
For the last 8 months, Higher Frequency Trading ported a legacy
C# application with over 25K lines of code to Java.
We have translated many LINQ statements into Java 8 Stream +
Lambda.
What are some common patterns and anti-patterns we have
seen?
16. To collect or not to collect
(anti-pattern)
getTrades().stream()
.filter(t -> getDate().equals(t.getInfo().getDate()))
.collect(toList())
.forEach(t -> trades.add(t.getInfo()));
17. To collect or not to collect
(solution)
List<TradeInfo> trades =
getTrades().stream()
.filter(t -> getDate().equals(
t.getInfo().getDate()))
.map(Trade::getInfo)
.collect(toList());
24. Optional for equals
public boolean equals(Object obj) {
return Optional.ofNullable(obj)
.filter(that -> that instanceof Test)
.map(that -> (Test)that)
.filter(that -> Objects.equals(this.s1, that.s1))
.filter(that -> Objects.equals(this.s2, that.s2))
.isPresent();
}
Posted by Marko Topolnik
25. Find the twenty most frequent words in a file
To use parallel or not?
List<String> words =
Files.lines(path).parallel()
.flatMap(line -> Arrays.asList(line.split("b")).stream())
.collect(groupingBy(w -> w, counting()))
.entrySet().stream()
.sorted(comparing(Map.Entry<String,Long>::getValue).reversed())
.limit(20)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
26. Lambdas and templates (before Java 8)
long value;
this.lock.lock();
try {
value = doSomething();
} finally {
this.lock.unlock();
}
27. Lambdas and templates (with Java 8)
public static <R> R with(Lock lock, Callable<R> work) {
lock.lock();
try {
return work.call();
} finally {
lock.unlock();
}
}
28. Lambdas and templates (with Java 8)
long value = with(lock, this::doSomething);
Note: with inlining, the temporary “Long” object can be eliminated.
29. Lambdas and performance
public void readMarshallable(Wire wire) {
wire.read(Fields.I).int32(x -> i = x)
.read(Fields.J).int32(x -> j = x)
.read(Fields.K).int32(x -> k = x)
.read(Fields.L).int32(x -> l = x)
.read(Fields.M).int32(x -> m = x)
.read(Fields.N).int32(x -> n = x)
.read(Fields.O).int32(x -> o = x)
.read(Fields.P).int32(x -> p = x);
}
32. Lambda type inference
• Java 8 uses type inference much more than before.
• Inference can appear to be as a cast.
// error type is not known.
Object o = () -> System.out::println;
// type is inferred.
Runnable o = () -> System.out::println;
// type is inferred not cast at runtime.
Object o = (Runnable & Serializable)
() -> System.out::println;
33. Lambda internals
• Classes for Lambdas are generated at runtime.
lambda.getClass() still works.
• The contents of code in a lambda is added as a static method,
except …
• When a lambda expression refers to a method e.g.
this::method or Class::new no additional method is created.
• Provided your code is inlined, the lambda “object” can be
eliminated. This also works for anonymous inner classes.
34. End randomly (Don’t try this at home)
IntStream.range(0, 128).parallel()
.forEach(System::exit);