James Gosling tells a great tale on why Sun failed. Spoiler: the alpha particles did it.
Tuesday, June 29, 2010
Monday, June 21, 2010
Competition for mindshare
Mindshare is a term I rarely hear outside of business or technology magazines, but is very appropriate here. Java has long been king in the unit testing space, and though hoary, JUnit is still one of the best unit testing libraries around.
But Ruby is giving Java serious competition. I am late to the party, but just ran across metric_fu in this post. I wish I had metric_fu for Java!
In the 90s the evolving Java ecosystem was the top reason I moved from C/C++ to Java. JDK7 looks to keep Java the language on life support so it will not immediate wither away (although who came up with the name public defender methods [PDF]?). But the library momentum is not in Java's favor at the moment.
Paul Holser's property-binder library
Paul Holser, a fellow ThoughtWorks alum, wrote a nice library for handling properties, property-binder
. More to the point, it is an example of PICA style (proxied interfaces configured with annotations), something Paul wrote about recently.
property-binder
works using dynamic proxies to turn one kind of call (a typed API) into another (lookups of properties). This is elegantly done.
An alternative for situations where proxies are not appropriate is to use the code-generation facility of annotations, to write Java code for the conversion. The user of annotations cannot tell the difference: the same annotations, interfaces and static factory methods appear in both cases.
This is an interesting corner of Java, bringing together several advanced features but leaving them accessible to the caller with elementary techniques.
Saturday, June 19, 2010
Tracking changing data in Java
I need to track changing data in Java. For example, say I am collecting data sets of some real world behavior, and I'd like to know what has changed between individual readings: additions, removals, changes.
I start with a single datum:
public static class Example { public final int index; // primary key public final double value; // satelite data public final long access; // other data public Example(final int index, final double value, final long access) { this.index = index; this.value = value; this.access = access; } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Example example = (Example) o; if (index != example.index) return false; return true; } @Override public int hashCode() { return index; } }
Reducing to essentials:
- Primary key values: different
Example
instances with the same index represent the same "thing" - Satellite data: these are the measurements I want to track
- "Other" data: these are part of my data set but are not part of my change tracking
Now equals
and hashCode
take care of matching equivalent data so I can compare values between data sets. But I also need to know if the satellite data has changed:
public static class ExampleComparator implements Comparator<Example> { @Override public int compare(final Example a, final Example b) { final int c = Integer.valueOf(a.index).compareTo(b.index); if (0 != c) return c; return Double.valueOf(a.value).compareTo(b.value); } }
An important point to consider: why not have Example
implement Comparable<Example>
instead of the separate comparator class? Read the Comparator
javadocs closely: you really want equals
and compareTo
to agree (the discussion is good — I have a new source of bugs to comb out of my code base).
But here equals
looks at only primary keys and the comparator looks at both primary keys and satellite data: they do not agree.
Having all the tools I need, time for comparing data sets (please be kind about the weak class name):
public class Differator<T, C extends Comparator<T>> { private final Map<T, T> items = new HashMap<T, T>(); private final C comparator; public static <T, C extends Comparator<T>> Differator<T, C> newDifferator( final C comparator) { return new Differator<T, C>(comparator); } public Differator(final C comparator) { this.comparator = comparator; } public Changes<T> replaceAll(final Set<T> newItems) { final Changes<T> changes = new Changes<T>(); for (final T item : items.keySet()) if (!newItems.contains(item)) { changes.deleted.add(item); items.remove(item); } for (final T newItem : newItems) { if (!items.containsKey(newItem)) changes.inserted.add(newItem); else { final T item = items.get(newItem); if (0 != comparator.compare(item, newItem)) changes.updated.add(new Delta<T>(item, newItem)); } items.put(newItem, newItem); } return changes; } public static class Delta<T> { public final T item; public final T newItem; public Delta(final T item, final T newItem) { this.item = item; this.newItem = newItem; } @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Delta delta = (Delta) o; if (!item.equals(delta.item)) return false; if (!newItem.equals(delta.newItem)) return false; return true; } @Override public int hashCode() { int result = item.hashCode(); result = 31 * result + newItem.hashCode(); return result; } } public static class Changes<T> { public final Set<T> inserted = new HashSet<T>(); public final Set<Delta<T>> updated = new HashSet<Delta<T>>(); public final Set<T> deleted = new HashSet<T>(); @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Changes changes = (Changes) o; if (!deleted.equals(changes.deleted)) return false; if (!inserted.equals(changes.inserted)) return false; if (!updated.equals(changes.updated)) return false; return true; } @Override public int hashCode() { int result = inserted.hashCode(); result = 31 * result + updated.hashCode(); result = 31 * result + deleted.hashCode(); return result; } } }
It was not much effort to write Differator
. replaceAll
swaps out a new data set for an old one, returning how they two differed. Credit for immediately spotting that Differator
is not merely thread unsafe, it is very unsafe. But this is not hard to address in a simple way.
Lastly, some tests:
public class DifferatorTest { private Differator<Example, ExampleComparator> differator; private Example item; private Changes<Example> changes; @Before public void setUp() throws Exception { differator = newDifferator(new ExampleComparator()); item = new Example(1, 3.14159, 123); changes = differator.replaceAll(singleton(item)); } @Test public void testFirstInsert() { assertThat(changes.inserted, is(equalTo(singleton(item)))); assertThat(changes.updated.isEmpty(), is(true)); assertThat(changes.deleted.isEmpty(), is(true)); } @Test public void testUpdateUnimportantData() { final Changes<Example> changes = differator .replaceAll(singleton(new Example(1, 3.14159, 456))); assertThat(changes.inserted.isEmpty(), is(true)); assertThat(changes.updated.isEmpty(), is(true)); assertThat(changes.deleted.isEmpty(), is(true)); } @Test public void testUpdateSatelliteData() { final Example newItem = new Example(1, 2.71828, 123); final Changes<Example> changes = differator .replaceAll(singleton(newItem)); assertThat(changes.inserted.isEmpty(), is(true)); assertThat(changes.updated, is(equalTo( singleton(new Differator.Delta<Example>(item, newItem))))); assertThat(changes.deleted.isEmpty(), is(true)); } @Test public void testUpdatePrimaryKey() { final Example newItem = new Example(2, 3.14159, 123); final Changes<Example> changes = differator .replaceAll(singleton(newItem)); assertThat(changes.inserted, is(singleton(newItem))); assertThat(changes.updated.isEmpty(), is(true)); assertThat(changes.deleted, is(singleton(item))); } }
Thursday, June 03, 2010
Summary of lambda changes for JDK7
Alex Blewitt writing on InfoQ posts an excellent write up on the state of lambda in JDK7.
It is exciting to see Oracle move on this front publicly after long months invisibility. Two interesting potential features I am rooting for: SAMs and this
for recursion.