September 24, 2003
Paid By the Keystroke?
On Monday evening, I did a bit of Ruby hacking. On Tuesday morning, I arrived at work and my first task involved iterating through a list, and doing something to each of its elements. My fingers were already typing list.each, and I had to wrench my brain from my Ruby mindset back to the Java Way.
Some time later in the day, I read Philip Greenspun's much-linked Java-is-an-SUV flame.
Problem: I have a list of objects. I want to create another list containing the ‘id’ property of those objects.
Solutions:1
- Ruby
list.map { |i| i.id }- Perl
map { $_->id } @list;- Python
[x.id for x in list]- Common Lisp (Corrected by Andreas)
(mapcar #'id list)- Smalltalk (from James)
list collect: [:each | each id]-
- OGNL (from Jason)
list.{id}- Java
List ids = new ArrayList(); for (Iterator i = list.iterator(); i.hasNext(); ) { ids.add(new Long(((Thingy)i.next()).getId())); }- Java w/ 1.5-style Generics/For Loop/Autoboxing
List<Long> ids = new ArrayList<Long>(); for (Thingy x :list) ids.add(x.getId());
Collection ids = CollectionUtils.collect(
x, new Transformer() {
public Object transform( Object thingy ) {
return ((Thingy)thingy).getId();
}
});1 I was too lazy to test that they work, but the syntax is close enough. Bug reports to /dev/null.
Posted to java, nerd at September 24, 2003 12:54 AMGreenspun redux: Unsurprisingly, there's been some link traffic generated from Greenspun's Java is the SUV... posting. I've see blog postings, and a thread in the SeaJUG mailing list, and the ultimate signal of success, and entry on the Bileblog. I've no problem
From: Ted Leung on the air at September 24, 2003 08:44 AMTrackback Testing Page: This is a link to a JIRA test issue:http://jira.atlassian.com/browse/TST2http://fishbowl.pastiche.org/2003/09/24/paidbythekeystroke
From: Confluence: Test Space at June 8, 2004 01:02 AMAnd with Generics, "ForEach" Loop and autoboxing you'd have:
List ids = new ArrayList();
for (Thingy x :list)
ids.add(x.getId());
For readability sake (meaning, I can look at the code and see what it is doing), I'd pick Java, followed (not so closely) by Perl. Of course, this may reflect the fact that I am not familiar with the other languages....
Posted by: LanceLavandowska at September 24, 2003 01:54 AM (#link)Python's list comprehensions rock, there is no doubt about it.
Posted by: Anthony Eden at September 24, 2003 02:17 AM (#link)Since Java has so many libraries (like commons-collections) TMTOWDI (apologies for formatting):
Collection ids = CollectionUtils.collect(
x, new Transformer() {
public Object transform( Object thingy ) {
return ((Thingy)thingy).getId();
}
} );
BTW, I think it's important to remember that judging readability is fairly worthless if you don't know the languages involved: 'map' is a common Perl idiom which, if you knew Perl, you'd certainly know. I'd hazard a guess that most of us don't maintain code in a language we don't know :-)
Posted by: Chris Winters at September 24, 2003 02:52 AM (#link)The Common Lisp code portion can even be shorter (and less irritatingly lambda-ridden (-:):
(mapcar #'id list)
Posted by: Andreas Fuchs at September 24, 2003 09:21 AM (#link)The conclusions I draw:
#1: bring on 1.5!
#2: (obviously) the one you know is easiest to understand.
Personally the 1.5 example seems to read easier than all except the Smalltalk and maybe the Python examples. But I'm biased of course, as per #2.
Posted by: Ian at September 24, 2003 09:48 AM (#link)In C++, assuming that 'mylist' is the list you are working on, and the elements are instances of 'myobj':
list<long> ids;
for (list<myobj>::const_iterator it = mylist.begin(); it != mylist.end(); ++ it)
ids.push_back(it->id);
Or if it's a list of pointers to instances of myobj:
list<long> ids;
for (list<myobj*>::const_iterator it = mylist.begin(); it != mylist.end(); ++ it)
ids.push_back((*it)->id);
It's hardly fair to compare statically-typed Java with a bunch of dynamically-typed languages. So how about adding a haskell version:
map thingy_id list
Posted by: Kimberley Burchett at September 25, 2003 06:21 AM (#link)The only "fair" comparison here is with C++. Java was designed to be a better C++, and it is.
Posted by: Alan Green at September 25, 2003 09:36 AM (#link)list.map( Thingy each => { return each.id; } );
Nice (statically-checked, JVM bytecodes, uses Java libraries) http://nice.sourceforge.net/index.html
In the Python example, 'list' need not actually be a materialised list but can be any iterable, so allowing the source items to be obtained lazily; with appropriate type declarations, the same could be true of the various Java examples. Nevertheless what is *produced* by the Python and Java versions given here will still be a materialised list. Python 2.4 will introduce generator expressions, which combine the ideas of generators and list comprehensions, so allowing the transformation of one iterable into another without materialising a list:
(x.id for x in list)
I imagine that the Java equivalent of this would be even longer than the examples given before.
Posted by: Hamish Lawson at April 29, 2004 01:39 AM (#link)