How often have you seen or written the same few lines of code that are constantly used together to solve the same problem? Take, for example, brute force or sorting (some manipulation) of collections. Programmers have to write such code sections every day. Of course, there are various IDEs in which you can use snippets and templates. Nevertheless, such constructions clutter up the code, making it more suitable for processing by a computer, and not for the programmer's perception.
This is
where the LambdaJ library comes to
our rescue . Its purpose is to simplify the process of working with collections in order to reduce errors in the code and increase its readability by implementing some functional programming techniques without neglecting the static typing of data. The latter fact is extremely important, since static typing is an advantage of the language, which significantly increases the reliability of the code.
Consider an example of using LambdaJ. Find the age of the youngest buyer, who made a purchase of more than 50'000.
')
Classic (iterative) method:
int age = Integer.MAX_VALUE;
for (Sale sale : sales) {
if (sale.getCost() > 50000.00) {
int buyerAge = sale.getBuyer().getAge();
if (buyerAge < age)
age = buyerAge;
}
}
LambdaJ method:
int age = min(forEach( select (sales, having(
on(Sale. class ).getCost(), greaterThan(50000.00)).getBuyer()))),
on(Person. class ).getAge());
Functionality
From the above example, it is clear that the library provides the programmer with a kind of
DSL for working with collections. Domains in this case are java-collections, and language constructs are statically imported methods of the corresponding classes of the LambdaJ library.
So what can LambdaJ do?
The list of features is approximately as follows:
- filtering elements by a given condition;
- change each element for a given rule;
- getting the specified property from each element;
- sorting items based on one of the properties;
- indexing items based on one of the properties;
- grouping items based on one or more properties;
- calling the specified method for each element;
- aggregation (eg, summation) of elements or one of their properties;
- projection (copying) of properties of one set of objects into properties of another set of objects (Domain -> DTO);
- concatenation of the string representation of elements or one of their properties.
Here you can add
work with closures (although, in my opinion, it’s better to wait for Java 7 here).
Examples
Filtration
List <Integer> biggerThan3 = filter(greaterThan(3), asList(1, 2, 3, 4, 5));
List <Person> oldFriends = filter(having(on(Person. class ).getAge(), greaterThan(30)), meAndMyFriends);
Aggregation
int totalAge = sum(meAndMyFriends, on(Person. class ).getAge());
Getting properties
List <Integer> lengths = convert(strings, new PropertyExtractor( "length" ));
List <Integer> ages = extract(persons, on(Person. class ).getAge());
Indexing
Map< String , Person> personsByName = index(persons, on(Person. class ).getFirstName());
Sorting
List <Person> sorted = sort(persons, on(Person. class ).getAge());
Grouping
Group<Person> group = group(meAndMyFriends, by(on(Person. class ).getAge()));
Group<Person> group29aged = group.findGroup( "29" );
Performance
As you know, the price for convenience is performance. In the case of LambdaJ, overhead averages 2.5 times [
* ]. This is definitely sad. However, if the manipulation of collections takes an insignificant part of the processor time of your application, then the advantages from the convenience of work can significantly outweigh the minus overhead.
PS
This article is a free translation of arbitrary parts of the
official LambdaJ Wiki . More information can be found there.