Streams and lambdas the good, the bad and the ugly

20
Streams and Lambdas – The Good, The Bad and the Ugly Peter Lawrey Higher Frequency Trading Ltd

description

Based on a six month migration of C# code to Java 8, what is legacy lambda code likely to look like and what mistakes can be made. Good use cases. Bad use cases with solutions Ugly use cases.

Transcript of Streams and lambdas the good, the bad and the ugly

Page 1: Streams and lambdas the good, the bad and the ugly

Streams and Lambdas – The Good, The Bad and the Ugly

Peter LawreyHigher Frequency Trading Ltd

Page 2: Streams and lambdas the good, the bad and the ugly

Introduction

For the last 6 months, Higher Frequency Trading has been porting 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?

Page 3: Streams and lambdas the good, the bad and the ugly

Contains 2.0

if (list.stream().anyMatch(p -> p.getType() == Type.Cash)) {

Page 4: Streams and lambdas the good, the bad and the ugly

Deep Copy

List<Position> newPositions = classPos.stream()

.map(Position::clone)

.collect(toList())

Page 5: Streams and lambdas the good, the bad and the ugly

Validate all entries

positions.forEach(Position::validate);

Validate throws an InvalidStateException if invalid

Page 6: Streams and lambdas the good, the bad and the ugly

Summing BigDecimal

BigDecimal sum = getResults().stream()

.reduce(BigDecimal.ZERO,

(bd, t) -> bd.add(t.getRequirement()),

BigDecimal::add);

Page 7: Streams and lambdas the good, the bad and the ugly

Sorting by multiple fields

setTrades(getTrades().stream()

.sorted(comparing(t -> t.getInfo().getDate())

.thenComparing(Position::getCUSIP)

.thenComparing(Position::getQuantity).reversed())

.collect(Collectors.toList()));

Page 8: Streams and lambdas the good, the bad and the ugly

Group By

Map<String, List<Position>> positionBySymbol =

positions.values().stream()

.filter(p -> p.getQuantity() != 0)

.collect(groupingBy(Position::getSymbol));

Page 9: Streams and lambdas the good, the bad and the ugly

Streaming Maps

pos.entrySet().stream()

.filter(p -> p.getValue().getQuantity() != 0.0)

.forEach(p -> pos2.put(p.getKey(), p.getValue()));

Page 10: Streams and lambdas the good, the bad and the ugly

To collect or not (anti-pattern)

getTrades().stream()

.filter(t -> getDate().equals(t.getInfo().getDate()))

.collect(toList())

.forEach(t -> trades.add(t.getInfo()));

Page 11: Streams and lambdas the good, the bad and the ugly

To collect or not (solution)

List<Trade> trades = getTrades().stream()

.filter(t -> getDate().equals(t.getInfo().getDate()))

.map(t → t.getInfo())

.collect(Collectors.toList());

Page 12: Streams and lambdas the good, the bad and the ugly

Sort of sorted (anti pattern)

Map<Date, List<Trade>> groupTrades =

trades.stream()

.sorted(comparing(Trade::getDate))

.collect(groupingBy(Trade::getDate));

Page 13: Streams and lambdas the good, the bad and the ugly

Sort of sorted (solution)

Map<Date, List<Trade>> groupTrades =

trades.stream()

.collect(groupingBy(

TradeDetail::getTradeDate,

TreeMap::new,

toList()));

Page 14: Streams and lambdas the good, the bad and the ugly

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());

Page 15: Streams and lambdas the good, the bad and the ugly

Multi-sorted (solution)

return trade.stream()

.filter(t -> !isExcluded(t))

.sorted(comparing(Trade::getNetAmount)

.thenComparing(Trade::getCUSIP)

.thenComparing(Trade::getDate))

.collect(toList());

Page 16: Streams and lambdas the good, the bad and the ugly

Top twenty words (Ugly)

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());

Page 17: Streams and lambdas the good, the bad and the ugly

I must use streams (Ugly)

combinedList.addAll(

balances.stream().collect(Collectors.toList()));

List<Trade> allTrades = new ArrayList<>();

trades1.forEach(t -> allTrades.add(t));

trades2.forEach(t -> allTrades.add(t));

Page 18: Streams and lambdas the good, the bad and the ugly

Optional denial

Position todayPos = newPos.stream()

.filter(pos -> pos.getCUSIP().equals(p.getCUSIP()))

.findFirst().orElse(null);

if (todayPos != null) {

Page 19: Streams and lambdas the good, the bad and the ugly

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) {

Page 20: Streams and lambdas the good, the bad and the ugly

Q & A

Peter Lawrey

http://vanillajava.blogspot.com/

@PeterLawrey

[email protected]