Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Pink & Squishy: Fun with Guava #2

Pink & Squishy: Fun with Guava #2

An ongoing series of presentations I made to show my workmates the wonders of Guava in a JRuby environment.

Chris Jester-Young

February 28, 2011
Tweet

More Decks by Chris Jester-Young

Other Decks in Programming

Transcript

  1. Review from last session • BiMap allows you to make

    two-way maps o Each key maps to a distinct value, and vice versa o Use "inverse map" to look up by value • Multimap allows many-to-many maps o Each key maps to a collection of values o Much easier to use than manually managing value collections o Can be inverted to look up by value
  2. Thread safety refresher • If an object is modified by

    two threads simultaneously: o The threads may not see each other's changes o The object may be left in an inconsistent state  Say a User object has date of birth and age fields  It may be possible to see inconsistent data between those two fields, when they're updated • One half of thread safety is about ensuring that these inconsistencies do not happen • The two main ways to ensure consistency are: o Mutual exclusion (synchronized blocks, Lock objects) o Atomic variables (volatile fields, Atomic* objects)
  3. Thread safety refresher • However, mutual exclusion must be used

    sparingly o Excessive synchronisation limits concurrency  Everything synchronised basically single-threaded ⇒  Risk of livelock o Improper use of synchronisation can cause deadlock • The other half of thread-safety is to ensure deadlock and livelock do not happen • Design programs to need as little synchronisation as feasible o Share less data between threads o Or, make shared data immutable
  4. Immutability refresher • An object is immutable if its (observable)

    state is fixed • In simple terms, this means: o All fields are final o Reference fields point only to immutable objects o (Fields for unobservable state are exempted) • Examples: Integer, String, Class, Pattern • Object's state is set during construction • After that, the state is locked in forever • New state, new object
  5. Why immutability? • Immutable objects can't change or become inconsistent

    o Objects can be shared among threads with impunity o Automatically thread-safe; no synchronisation required o No moving parts simpler, easier to understand ⇒ • Better performance in many cases o No synchronisation required more concurrency ⇒ o No defensive copying required o "Modifying" immutable objects requires making copies  Cost usually small, unless you copy frequently  Profile and see if usage pattern causes problems  In most cases, copying is not the bottleneck
  6. Immutable collections • In Guava, immutable collections are the preferred

    way to work with collections • Guava has a big selection of immutable collections: o ImmutableList o ImmutableMap, ImmutableSortedMap, ImmutableBiMap o ImmutableSet, ImmutableSortedSet, ImmutableEnumSet o ImmutableMultimap, ImmutableListMultimap, ImmutableSetMultimap o ImmutableMultiset
  7. Immutable collections • Assuming the elements are also immutable, the

    collection contents will never change o Immutable collections are completely independent from the collections they copy from o This is a contrast from Java's unmodifiable collections: array = [3, 1, 4, 1, 5, 9].to_java unmodifiable = Collections.unmodifiable_list Arrays.as_list(array) immutable = ImmutableList.copy_of array array[0] = 6 unmodifiable # [6, 1, 4, 1, 5, 9] immutable # [3, 1, 4, 1, 5, 9]
  8. Three ways to Sunday • There are three ways to

    create any immutable collection • If the items are known a priori, use the .of factory method ImmutableList.of 3, 1, 4, 1, 5, 9 # [3, 1, 4, 1, 5, 9] ImmutableSet.of 3, 1, 4, 1, 5, 9 # [3, 1, 4, 5, 9] ImmutableSortedSet.of 'foo', 'bar', 'baz' # [bar, baz, foo] ImmutableMap.of :foo, 3, :bar, 1, :baz, 4 # {foo=3, bar=1, baz=4} ImmutableMap.of :foo, 3, :foo, 1 # IllegalArgumentException • For unsorted collections, the insertion order is used o Ordering not unlike LinkedHashMap/LinkedHashSet
  9. Three ways to Sunday • Another method is to use

    a builder: builder = ImmutableList.builder builder.add 3, 1 builder.add 4, 1, 5 list = builder.build # [3, 1, 4, 1, 5] • Items can be added incrementally, such as in a loop: builder = ImmutableMap.builder 'foo=bar&bar=baz&baz=quux'.split('&').each do |x| builder.put *x.split('=') end map = builder.build # {foo=bar, bar=baz, baz=quux}
  10. Three ways to Sunday • To use the contents of

    an existing collection, use .copyOf: ImmutableList.copy_of [3, 1, 4, 1, 5, 9] # [3, 1, 4, 1, 5, 9] ImmutableSet.copy_of [3, 1, 4, 1, 5, 9] # [3, 1, 4, 5, 9] ImmutableMap.copy_of 'foo'=>3, 'bar'=>1, 'baz'=>4 # {foo=3, bar=1, baz=4} • Despite the name, no copying is actually done if the incoming collection is already immutable o Perfect for "on-demand" defensive copying
  11. Defensive copying refresher • Defensive copying ensures that an object's

    state cannot be messed with by code outside the object's class • Example of code without defensive copying: private Date dob; public Date getDob() { return dob; } public void setDob(Date dob) { if (!checkCoppa(dob)) throw new UnderageException(); this.dob = dob; }
  12. Defensive copying refresher • What happens when we fail to

    defensively copy? • Important checks in the setter can be skipped. Two ways: o Through the getter: Date dob = user.getDob(); dob.setYear(...); o Through the setter: user.setDob(dob); dob.setYear(...); • To mitigate this: o The getter must return a copy of the field's value o The setter must copy the argument before storing • These copies are called defensive copies
  13. Defensive copying refresher • Defensive copies ensure that no aliases

    to internal mutable state leak outside the class o Aliases allow outside code to break class invariants • Fixed code: public Date getDob() { return new Date(dob.getTime()); } public void setDob(Date dob) { dob = new Date(dob.getTime()); if (!checkCoppa(dob)) throw new UnderageException(); this.dob = dob; }
  14. Immutable collection fields • Immutable objects never need defensive copying

    • Use .copyOf to make immutable copy of a given collection: attr_reader :properties def properties= p @properties = ImmutableMap.copy_of p end o No copying happens if the given collection is already immutable o In Java code, you can cut the .copyOf call out of the setter by taking an immutable collection argument  This makes your API less user-friendly, so use this micro-optimisation with care
  15. Recap • There are many great reasons to use immutable

    objects o They make your programs easier to understand o They make your programs easier to parallelise o They eliminate the need for defensive copying • Guava provides a rich set of immutable collections o They're one of the cornerstones of Guava's facilities o Immutable collections are not aliased to any mutable collections o There are three ways to create each collection o Perfect for use in fields  Setters can use .copyOf willy-nilly, as copying is skipped if the new value is already immutable