Legacy lambda code

35
Javaland 2015 Peter Lawrey Legacy Lambda Code in Java

Transcript of Legacy lambda code

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 fileTo 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