Mario has posted 14 posts at DZone. You can read more from them at their website. View Full User Profile

Manipulating Collections Without Loops With lambdaj

06.23.2009
| 11419 views |
  • submit to reddit

How many times have you read or written the same two or three lines of code that frequently seem to go together, and even though they operate on different objects, feel like the same thing? And how often these repetitions involve some sort of collections iteration or more generically manipulation? These repetitions in the code is something that developers eventually learn to filter out and ignore when reading code, once they figure out where the interesting parts are placed. But even if the developers get used to it, it slows them down. Code like that is clearly written for computers to execute, not for developers to read.

lambdaj is a library that makes easier to address this issue by allowing to manipulate collections in a pseudo-functional and statically typed way. In our experience to iterate over collection, especially in nested loops, is often error prone and makes the code less readable. The purpose of this library is to alleviate these problems employing some functional programming techniques but without losing the static typing of java. We impose this last constraint to make refactoring easier and safer and allow the compiler to do its job.

Access collections without explicit loops

The main purpose of lambdaj is to partially eliminate the burden to write (often nested and poorly readable) loops while iterating over collections. In particular it allows to iterate collections in order to:

    * filter its items on a given condition
    * convert each item with a given rule
    * extract a given property from each item
    * sort the items on the values of one of their property
    * group or index the items on the value of one or more properties
    * invoke a method on each item
    * sum (or more generally aggregate) the items or the values of one of their property
    * concatenate the string representation of the items or of the values of one of their property

without to write a single explicit loop.

How does lambdaj work?

There are 2 ideas at the base of lambdaj. The first one is to treat a collection of objects as it was a single object by allowing to propagate a single method invocation to all the objects in the collection as in the following example:

List<Person> personInFamily = asList(new Person("Domenico"), new Person("Mario"), new Person("Irma"));
forEach(personInFamily).setLastName("Fusco");


In this example all the persons in the list belongs to the same family so they all have the same last name. The forEach method actually returns a proxy object that implements both the Iterable interface and all the methods in each object in the given list. That allows to invoke a method of the Person object on the object returned by the forEach method as it was an instance of the Person class. When you do that, under the hood lambdaj propagates your invocation to all the objects in the collection.

The second idea on which lambdaj is built on is the possibility to have a pointer to a java method in a statically typed way by using the lambdaj's on construct. That allows to easily and safely define on which argument a given lambdaj feature has to be applied. For example in the following statement we used the on construct in order to say the argument (their respective ages) on which a list of persons has to be sorted:

List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge()); 

Comparing this last statement with the piece of code necessary to achieve the same result in plain Java:

List<Person> sortedByAgePersons = new ArrayList<Person>(persons);
Collections.sort(sortedByAgePersons, new Comparator<Person>() {
        public int compare(Person p1, Person p2) {
           return Integer.valueOf(p1.getAge()).compareTo(p2.getAge());
        }
});


makes evident how lambdaj could improve both the productivity while writing code and probably even more important its readability when you are called to maintain it.

lambdaj features

As stated lambdaj is designed to easily manipulate collections. Its features are intended to filter, convert, index and aggregate the items of a collection without explicitly iterate on it. Moreover the lambdaj API are designed to be easily concatenated in order to jointly use two or more features in a single statement. To investigate these features in more details and possibly to download and start using lambdaj, check it out at:

http://code.google.com/p/lambdaj/

Published at DZone with permission of its author, Mario Fusco.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Aravind Yarram replied on Tue, 2009/06/23 - 8:49am

tried using it in my application. very nice framework but few things doenst seem to work and I didnt find any forum to seek help

 

List<Person> oldFriends = filter(having(on(Person.class).getName(), endsWith("don")), meAndMyFriends);

 

Aravind Yarram replied on Tue, 2009/06/23 - 8:48am

The endsWith , startsWith() methods doesnt seem to work...not sure why?

Mario Fusco replied on Tue, 2009/06/23 - 9:21am in response to: Aravind Yarram

Hi,

if you read the home page documentation of the project until the end you can easily understand why those methods don't work. I'm copying and paste the relevant part of it hoping that it helps:

Known limitations

lambdaj has a known limitation in both of its main features due to 2 different issues of the java language: lack of reified generics and impossibility to proxy a final class.

IIn more details the first problem doesn't allow lambdaj to infer the actual type that should be returned by the forEach() method when a null or empty collection is passed to it. That means that the following piece of code:

List<Person> persons = new ArrayList<Person>();
forEach(persons).setLastName("Fusco");

will cause lambdaj to throw an exception because it is not able to decide which actual Class should be proxied. To overcome this problem, when you aren't sure that a given collection has at least one item in it, it is advised to use the forEach() overloaded method that accepts a Class as second parameter as it follows:

forEach(persons, Person.class).setLastName("Fusco");

The second issue caused that when the on() method meets a final class it cannot proxy the returned value. It means that for example the following statement will not work:

List<Person> sortedByNamePersons = sort(persons, on(Person.class).getName().toLowerCase()); 

Even in this case lambdaj will throw an exception complaining that the requested argument cannot be resolved. To fix this last problem it is necessary to find some workaround in order to "help" lambdaj to work as you expect, probably by adding the method getLowerCaseName() to the Person class.

Aravind Yarram replied on Tue, 2009/06/23 - 11:16am in response to: Mario Fusco

ths for the reply but I dont see any exceptions being thrown....the filter doesnt just return anything...

Mario Fusco replied on Tue, 2009/06/23 - 11:47am

Sorry, I didn't completely read the statement you send on your first post because it was partially hidden. I thought you were calling the endsWIth() method on the String Class instead of using the hamcrest matcher as you correctly did.

Just a quick question: is the having() matcher the one defined in the class ch.lambdaj.function.matcher.HasArgumentWithValue ?

If the answer is yes, actually the statement you wrote should work exacly as you expected:it should return all the persons in your list having a name that ends with "don". If it doesn't, could you please send me the complete code you used in your test so I can try it by myself?

Thanks a lot
Mario

Ali Yang replied on Tue, 2009/06/23 - 8:59pm

How about the performance of LambdaJ?

Mario Fusco replied on Wed, 2009/06/24 - 2:11am in response to: Ali Yang

lambdaj makes a large use of proxy (through the cglib library) and reflectiontion and that of course have a big impact on performance. In our experience however they are never the bottleneck of a real world application. Anyway if you download my presentation from the project site:

http://lambdaj.googlecode.com/files/lambdaj.pdf

you will find that the last slide contains a full comparision of the time needed to perform the same tasks with and without lambdaj.

sub online replied on Fri, 2009/06/26 - 2:33am

It's a deal.links of londonIt's easy for me.No such thing.Don't blow it.Tiffany JewelleryBuy one get one free.links of londonCan't be picky (about food).Be practical.I don't know him well.Treat it with respect.Have respect for someone.It takes two to tango.I'll pretend I didn't see that.That goes without saying.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.