JavaOne- Lambdas Chops: Recipes for simpler more expressive code

“Lambdas Chops: Recipes for simpler more expressive code”

Speaker: Ian Robertson
slidehare.net/nainostrebor

For more blog posts from JavaOne, see the table of contents


Disclaimer: These are now the most performant way of doing things. If you have I/O or database code, it won’t be a significant difference.

Did one slide review of lambdas as “test” for whether kow enough to follow rest of session. [good idea]

Null safe navigation

  • In Groovy, a?.getX()?.getY()
  • Call method if object is non-null, otherwise just return null
  • Best route is to refactor to return Optional from the getters
  • If can’t refactor: Optional.ofNullable(employee).ap(Employee::getAddress).map(Address::getCity).orElse(null)
  • By chaining Optional can decide what to do

First non null

  • commons-lang has ObjectUtils.firstNonNull
  • Works will if values are cheap to provide
  • If expensive/time consuming, waiting for wasted work
  • firstNonNull(customer.getCart():getRecommendations, this::getBestSellers)
  • and firstNonNull now would have- Stream.of(values).map(Supplier:get).filter(Objects:nonNull).findFIrst().orElse(null)

Multi-method interfaces

  • Helper methods can create instances from one or more lambdas
  • Showed with SimpleFieVisitor. [i don’t see a lambda or method reference here. He mentioned a lambda, but there are no abstract methods now]

Exceptions

  • forEach(File::createNewFile) – doesn’t compile because throws IOException
  • Tried to solve in language and couldn’t.
  • Can catch exception in lambda and convert to runtime exception, but now code is long
  • jOOL library create companion interface that allows checked exceptions and converts to runtime exception
  • now write forEach(Unchecked.consumer(File::createNewFile))

Design Pattern

  • Dispose pattern – close in proper place [no null check in example]
  • Try-with-resources added in Java 7
  • Can forget; poor API that lets you forget
  • Must implement AutoCloseable
  • Can have close logic or lock/unlock in one place and pass lambda [still have to remember to call that]
  • Vavr (Java upside down) library has good library for lazy evaluation (double checked locking pattern) – pass lambda wih work

Type safety

  • Many APIs rely on String representation of method names
  • ex: commmons-beanutils takes method name for comparators. “stringly typed”
  • JDK added API that takes method reference Comparator.comparing(Person::getLastName).thenComparing(Person:getFirstName)
  • Now type safe

JUnit 5 Parameterized Tests

  • @MethodSource has “stringly typed” problem. Also, no way to know passing right # parameters and types
  • Alternatively can use @TestFactory and DynamicTests to have compile time type safety
  • Harder to read with tuples. Can improve further with helper method per combo and have tests use it.
  • [the lack of type safety doesn’t seem like a big deal here since run tests regularly so would know if mismatch. And parameterized tests are much easier to read ]
  • Wrote LambdataRunner to do this in JUnit 4

Best Practices

  • Have more single method interfaces. Provide static helper methods to create
  • Use existing interfaces where can instead of writing own. Easier because already know what it is
  • Avoid overloading method names to accept different lambas of same “shape”. Compiler says lamda ambigious and require lients to cast

My take: I really liked the discussion of pros and cons. And the emoticons were fun – happy, sad, angry, meh. I don’t like the JUnit example, but the rest sound like things I’d do/try.

JavaOne – Make your CPU cores sweat with Parallel Streams

“Make your CPU cores sweat with Parallel Streams”

Speaker: Lukasz Pater

For more blog posts from JavaOne, see the table of contents


Started with the canonical Person/Car/age example to show what streams are [I think everyone at Javaone knows that]

Then showed code that finds all the prims under 10 milion that are palindromes when expressed in binary. Moores law is now relying on multi-core architecture. This example takes 1.6 seconds vs 8.6 seconds as parallel improvement. Five times faster.

Good analogy: If told to wash the fork, then the knife, then the… it is slow. If told to wash the dishes, you can optimize internally.

History of threads

  • JDK 1 – Threads – still good for small background task
  • JDK 5 – ExecutorService, concurrent objects
  • JDK 7 – fork/join framework – recursively decompose tasks into subtasks and then combine results
  • JDK 8 – parallel streams – use fork/join framework and Spliterator behind the scenes

Making a parallel stream

  • parallelStream() – for source
  • parallel() – anywhere in stream pipeline intermediate operation list

Fork Join Pool

  • Uses work stealing to balance tasks amongst workers in pool
  • All parallel streams use one common pool instance with # threads = # CPU cores – 1. That final thread is for the master to assign work.
  • Can change by setting system property java.util.concurrent.ForkJoinPool.common.parallelism. Must pass on commands line because first call to parallel stream resets it if set in code
  • If want custom fork join pool, create one and submit your stream to it. Does not recommend doing this. One reason you might want to is to add a timeout to the stream

Warnings

  • Avoid IO – burn CPU cycles waiting for IO/network
  • Use only for CPU intensive tasks
  • Be careful with nested paralle streams
  • Having many smaller tasks in the pool will better balance the workload
  • Don’t create your own fork join pool

Spliterator

  • splitable iterator
  • to traverse elements of a source in parallel
  • tryAdvance(Consumer) – do something if an element exists
  • trySplit() – partition off some elements to another spliterator leaving less elements in the original – fork so have tree of spliterators until run out of elements
  • characteristics()
  • estimateSize()
  • StreamSupport.stream(mySplierator, true) – creates parallel stream from spliterator – shouldn’t need to do this
  • ArrayList decomposes into equal sizes. LinkedList gives a smaller % of the elements because linear to get elements and want to minimize wait time
  • ArrayList and IntStream.range decompose well
  • LInkedLIst and Stream.iterate() decompose poorly – could even run out of memory
  • HashSet and TreeSet decompose in between

Other tips

  • Avoiding autoboxing also saves time. iterate() creates boxed objects where range() creates primitives
  • Parallel streams perform better where order doesn’t atter. findAny() or unordered().limit() [he missed the terminal operation in the limit example]
  • Avoid shared state
  • If have multiple calls to sequential() and parallel(), the last one wins and takes effect for the entire stream pipeline

My take:
Good discussion of performance and things to be beware of. My blog wasn’t live becase I couldn’t get internet in the room. I typed it live though! A couple typos like findFist() but nothing signficiant

JavaOne – Streams in JDK 8

“Streams in Java 8 – The good, the bad & the ugly”

Speaker: Simon Ritter & Stuart Marks
[Simon has the Twtter handle @speakjava; very cool]

For more blog posts from JavaOne, see the table of contents


Need to think differently. We are used to imperative programming with loops and variables.

Dealing wih exceptions
ugly code – three lines of code and hard to tell what it does

Problems:

  • looks like Perl
  • returns null (vs Optional or empty string)
  • split is called twice so wasted work
  • skipped URLDecoder.decode() because didn’t want to deal with a checked exception – but lost functionality. Problem caused by a missing API in Java so have to use decode.

Better approach:

  • use a method with a try/catch block; call that method from the stream
  • use Map.entry to simulate a tuple
  • Use single char (vs regex) in split. If only pass one character to split, far faster
  • split() is overloaded to take a numeric limit to how many are returned

Imperative streams
inside the for each is a print, and if statement and a LongAdder variable (good for frequent writes and infrequent reads)

then refactored to use mapToInt, a println and an if statement and a local variable. more complicated and still not functional

then switched to peek and no variabe but still an if statement (well a ternary)

finally switched to use a filter and count instead of sum

still not 100% functional because println is a side effect. ok for debugging

[good showing evolution to get functional]

Problems

  • Easy to misuse forEach() because feels familiar. But easily leads to side effects
  • Imperative thinking “for each of these I want to..”
  • Pause to consider if should use for each

Mixing internal and external iteration
for loop running 12 times and then getting data for each month with filter checking Month.of(x) – doesn’t work because x isn’t effectively final

“solve” effectively final by setting to different interim variable

IntStream.range(0,12).forEach – uses internal iteration but forEach. Marginaly better as don’t need interim variable

Instead return a nested map of Month to Map with nested grouping by so only need one iteration – the data stream

Problems

  • Going through data.stream() 12 times
  • forEach cheat
  • array not right data structure; it’s really a map of month to value

Hands on lab question
reduce (“”, (a,b) -> a+b) – works but inefficient because String concatenation

reduce(a,b) -> sb.append(b) – fails because ignores the first letter.

next attempt uses an if statement in reduce

then tried a custom collector. works but more complicated than necessary
Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString

or just use Collectors.joining()

Problems

  • If not using a parameter, it is probably wrong
  • Side effects
  • if stateent version not associative so would fail when run in parallel

Misc

  • can’t use same stream multiple times
  • method references are slightly more efficient than lambdas because lambda gets added into a method in bytecode. Saves a level of indirection by using method reference. But only slightly
  • Calling .sorted() multiple times vs chaining comparing.thenComparing – the later is better [also works because preserves sort :)]
  • parallel streams do more work. might or might not complete faster. uses fork-join pool. number of threads defaults to number of CPUs. In Java9, this is # CPUs for container. On Jaa 8, it was for physical machine
  • Nested parallel streams is bad idea because using same threads so performance is worse. Can create ForkJoinPool if must. Buyer beware; this is an implementation specific behavior and tied to the profile of the machine you write it for.

My take: Fun start to he morning. I like that they covered common things in an entertaining way and not common things. Something to learn for everyone!