KEMBAR78
Legacy lambda code | PPTX
Javaland 2015
Peter Lawrey
Legacy Lambda Code in Java
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.
Counting elements in a Stream
long count = list.stream().count();
Counting elements in a Stream
long count = list.stream().count();
// Stream.count()
@Override
public final long count() {
return mapToLong(e -> 1L).sum();
}
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);
}
Counting elements in a Stream
long count = list.parallelStream().count();
When the examples get more complex, lambdas become much
more interesting.
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?
Summing BigDecimal
getResults().stream()
.reduce(BigDecimal.ZERO,
(bd, t) -> bd.add(t.getRequirement()),
BigDecimal::add);
Validate all entries
positions.stream().forEach(Position::validate);
Validate throws an exception.
Sorting by multiple fields.
setTrades(trades.stream()
.sorted(comparing(t -> t.getInfo().getDate())
.thenComparing(Position::getCUSIP)
.thenComparing(Position::getQuantity)
.reversed())
.collect(toList()));
Sorting by multiple fields,
Quantity reversed
setTrades(trades.stream()
.sorted(comparing(t -> t.getInfo().getDate())
.thenComparing(Position::getCUSIP)
.reversed()
.thenComparing(Position::getQuantity)
.reversed())
.collect(toList()));
Group By
Map<String, List<Position>> positionBySymbol =
positions.values().stream()
.filter(p -> p.getQuantity() != 0)
.collect(groupingBy(Position::getSymbol));
Streaming Maps
pos.entrySet().stream()
.filter(p -> p.getValue().getQuantity() != 0.0)
.forEach(p -> pos2.put(p.getKey(), p.getValue()));
Contains 2.0
if (list.stream()
.anyMatch(p -> p.getType() == Type.Cash)) {
Deep copy
List<Position> newPositions =
classPos.stream()
.map(Position::clone)
.collect(toList())
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()));
To collect or not to collect
(solution)
List<TradeInfo> trades =
getTrades().stream()
.filter(t -> getDate().equals(
t.getInfo().getDate()))
.map(Trade::getInfo)
.collect(toList());
Sort of sorted (anti-pattern)
Map<Date, List<Trade>> groupTrades = trades.stream()
.sorted(comparing(Trade::getDate))
.collect(groupingBy(Trade::getDate));
Sorting (solution)
Map<Date, List<Trade>> groupTrades = trades.stream()
.collect(groupingBy(
TradeDetail::getTradeDate,
TreeMap::new,
toList()));
Multi-sorted (anti-pattern)
return trade.stream()
.filter(t -> !isExcluded(t))
.sorted(comparing(Trade::getDate))
.sorted(comparing(Trade::getCUSIP))
.sorted(comparing(Trade::getNetAmount))
.collect(toList());
See slide 2 example for solution.
Concurrent removal
? anti-pattern ?
input.stream()
.filter(t -> t.getParent() == p.getParent())
.forEach(input::remove);
Optional Denial
Position todayPos = newPos.stream()
.filter(pos -> pos.getCUSIP()
.equals(p.getCUSIP()))
.findFirst().orElse(null);
if (todayPos != null) {
Optional Denial
Optional<MTrade> otodayTrade = trades.stream()
.filter(t -> t.getCUSIP().equals(p.getCUSIP()))
.findFirst();
MTrade todayTrade = null;
if (otodayTrade.isPresent()) todayTrade = otodayTrade.get();
if (todayTrade != null && todayTrade.getClosingPrice()!=null){
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
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());
Lambdas and templates (before Java 8)
long value;
this.lock.lock();
try {
value = doSomething();
} finally {
this.lock.unlock();
}
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();
}
}
Lambdas and templates (with Java 8)
long value = with(lock, this::doSomething);
Note: with inlining, the temporary “Long” object can be eliminated.
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);
}
Lambdas and performance
garbage in bytes per lambda
Lambdas and performance
-XX:BCEATraceLevel=3
Prints messages like
Skipping method because: code size (271) exceeds
MaxBCEAEstimateSize (150).
So I raised the
-XX:MaxBCEAEstimateSize=300
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;
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.
End randomly (Don’t try this at home)
IntStream.range(0, 128).parallel()
.forEach(System::exit);
Q & A
http://vanillajava.blogspot.com/
Peter Lawrey
@PeterLawrey

Legacy lambda code

  • 1.
  • 2.
    Agenda • Lambdas: Acomplicated way to do something simple? • Lambda Patterns we used. • Lambda Patterns we shouldn’t have used. • Lambda Patterns we have used since.
  • 3.
    Counting elements ina Stream long count = list.stream().count();
  • 4.
    Counting elements ina Stream long count = list.stream().count(); // Stream.count() @Override public final long count() { return mapToLong(e -> 1L).sum(); }
  • 5.
    Counting elements ina 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 ina Stream long count = list.parallelStream().count(); When the examples get more complex, lambdas become much more interesting.
  • 7.
    Porting a legacyC# 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?
  • 8.
    Summing BigDecimal getResults().stream() .reduce(BigDecimal.ZERO, (bd, t)-> bd.add(t.getRequirement()), BigDecimal::add);
  • 9.
  • 10.
    Sorting by multiplefields. setTrades(trades.stream() .sorted(comparing(t -> t.getInfo().getDate()) .thenComparing(Position::getCUSIP) .thenComparing(Position::getQuantity) .reversed()) .collect(toList()));
  • 11.
    Sorting by multiplefields, Quantity reversed setTrades(trades.stream() .sorted(comparing(t -> t.getInfo().getDate()) .thenComparing(Position::getCUSIP) .reversed() .thenComparing(Position::getQuantity) .reversed()) .collect(toList()));
  • 12.
    Group By Map<String, List<Position>>positionBySymbol = positions.values().stream() .filter(p -> p.getQuantity() != 0) .collect(groupingBy(Position::getSymbol));
  • 13.
    Streaming Maps pos.entrySet().stream() .filter(p ->p.getValue().getQuantity() != 0.0) .forEach(p -> pos2.put(p.getKey(), p.getValue()));
  • 14.
    Contains 2.0 if (list.stream() .anyMatch(p-> p.getType() == Type.Cash)) {
  • 15.
    Deep copy List<Position> newPositions= classPos.stream() .map(Position::clone) .collect(toList())
  • 16.
    To collect ornot to collect (anti-pattern) getTrades().stream() .filter(t -> getDate().equals(t.getInfo().getDate())) .collect(toList()) .forEach(t -> trades.add(t.getInfo()));
  • 17.
    To collect ornot to collect (solution) List<TradeInfo> trades = getTrades().stream() .filter(t -> getDate().equals( t.getInfo().getDate())) .map(Trade::getInfo) .collect(toList());
  • 18.
    Sort of sorted(anti-pattern) Map<Date, List<Trade>> groupTrades = trades.stream() .sorted(comparing(Trade::getDate)) .collect(groupingBy(Trade::getDate));
  • 19.
    Sorting (solution) Map<Date, List<Trade>>groupTrades = trades.stream() .collect(groupingBy( TradeDetail::getTradeDate, TreeMap::new, toList()));
  • 20.
    Multi-sorted (anti-pattern) return trade.stream() .filter(t-> !isExcluded(t)) .sorted(comparing(Trade::getDate)) .sorted(comparing(Trade::getCUSIP)) .sorted(comparing(Trade::getNetAmount)) .collect(toList()); See slide 2 example for solution.
  • 21.
    Concurrent removal ? anti-pattern? input.stream() .filter(t -> t.getParent() == p.getParent()) .forEach(input::remove);
  • 22.
    Optional Denial Position todayPos= newPos.stream() .filter(pos -> pos.getCUSIP() .equals(p.getCUSIP())) .findFirst().orElse(null); if (todayPos != null) {
  • 23.
    Optional Denial Optional<MTrade> otodayTrade= trades.stream() .filter(t -> t.getCUSIP().equals(p.getCUSIP())) .findFirst(); MTrade todayTrade = null; if (otodayTrade.isPresent()) todayTrade = otodayTrade.get(); if (todayTrade != null && todayTrade.getClosingPrice()!=null){
  • 24.
    Optional for equals publicboolean 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 twentymost 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 publicvoid 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); }
  • 30.
  • 31.
    Lambdas and performance -XX:BCEATraceLevel=3 Printsmessages like Skipping method because: code size (271) exceeds MaxBCEAEstimateSize (150). So I raised the -XX:MaxBCEAEstimateSize=300
  • 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 • Classesfor 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’ttry this at home) IntStream.range(0, 128).parallel() .forEach(System::exit);
  • 35.