Nothing Special   »   [go: up one dir, main page]

Lambdaj

Download as pdf or txt
Download as pdf or txt
You are on page 1of 21

ambdaj

An internal DSL to manipulate collections without loops


by Mario Fusco mario@exmachina.ch

What is lambdaj for?


It provides a DSL to manipulate collections in a pseudo-functional and statically typed way. It eliminates the burden to write (often poorly readable) loops while iterating over collections. It allows to iterate collections in order to: convert filter aggregate index

group
sort

extract

How does lambdaj work?


lambdaj is a thread safe library of static methods based on 2 main features: treat a collection as it was a single object by allowing to propagate a single method invocation to all the objects in the collection forEach(personsInFamily).setLastName("Fusco"); allow to define a pointer to a java method in a statically typed way sort(persons, on(Person.class).getAge());

Thats all you need to know to start using lambdaj

It sounds easy, isnt it?

If you dont believe me lets see some

EXAMPLEs & DEMOs

The Demo Data Model

Print all cars brands


Iterative version:
StringBuilder sb = new StringBuilder(); for (Car car : db.getCars()) sb.append(car.getBrand()).append(", "); String brands = sb.toString().substring(0, sb.length()-2);

lambdaj version:
String brands = joinFrom(db.getCars()).getBrand();

Select all sales of a Ferrari


Iterative version:
List<Sale> salesOfAFerrari = new ArrayList<Sale>(); for (Sale sale : sales) { if (sale.getCar().getBrand().equals("Ferrari")) salesOfAFerrari.add(sale); }

lambdaj version:
List<Sale> salesOfAFerrari = select(sales, having(on(Sale.class).getCar().getBrand(),equalTo("Ferrari")));

Find buys of youngest person


Iterative version:
Person youngest = null; for (Person person : persons) if (youngest == null || person.getAge() < youngest.getAge()) youngest = person; List<Sale> buys = new ArrayList<Sale>(); for (Sale sale : sales) if (sale.getBuyer().equals(youngest)) buys.add(sale);

lambdaj version:
List<Sale> sales = select(sales,having(on(Sale.class).getBuyer(), equalTo(selectMin(persons, on(Person.class).getAge()))));

Find most costly sale


Iterative version:
double maxCost = 0.0; for (Sale sale : sales) { double cost = sale.getCost(); if (cost > maxCost) maxCost = cost; }

lambdaj version:
Sol. 1 -> double maxCost = max(sales, on(Sale.class).getCost()); Sol. 2 -> double maxCost = maxFrom(sales).getCost();

Sum costs where both are males


Iterative version:
double sum = 0.0; for (Sale sale : sales) { if (sale.getBuyer().isMale() && sale.getSeller().isMale()) sum += sale.getCost(); }

lambdaj version:
double sum = sumFrom(select(sales, having(on(Sale.class).getBuyer().isMale()).and( having(on(Sale.class).getSeller().isMale())))).getCost();

Find age of youngest who bought for more than 50,000


Iterative version:
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 version:
int age = min(forEach(select(sales, having(on(Sale.class).getCost(), greaterThan(50000.00)))) .getBuyer(), on(Person.class).getAge());

Sort sales by cost


Iterative version:
List<Sale> sortedSales = new ArrayList<Sale>(sales); Collections.sort(sortedSales, new Comparator<Sale>() { public int compare(Sale s1, Sale s2) { return Double.valueOf(s1.getCost()).compareTo(s2.getCost()); } });

lambdaj version:
List<Sale> sortedSales = sort(sales, on(Sale.class).getCost());

Extract cars original cost


Iterative version:
List<Double> costs = new ArrayList<Double>(); for (Car car : cars) costs.add(car.getOriginalValue());

lambdaj version:
List<Double> costs = extract(cars, on(Car.class).getOriginalValue());

Index cars by brand


Iterative version:
Map<String, Car> carsByBrand = new HashMap<String, Car>(); for (Car car : db.getCars()) carsByBrand.put(car.getBrand(), car);

lambdaj version:
Map<String, Car> carsByBrand = index(cars, on(Car.class).getBrand());

Group sales by buyers and sellers


(iterative version)
Map<Person,Map<Person,Sale>> map = new HashMap<Person,Map<Person,Sale>>(); for (Sale sale : sales) { Person buyer = sale.getBuyer(); Map<Person, Sale> buyerMap = map.get(buyer); if (buyerMap == null) { buyerMap = new HashMap<Person, Sale>(); map.put(buyer, buyerMap); } buyerMap.put(sale.getSeller(), sale); } Person youngest = null; Person oldest = null; for (Person person : persons) { if (youngest == null || person.getAge() < youngest.getAge()) youngest = person; if (oldest == null || person.getAge() > oldest.getAge()) oldest = person; } Sale saleFromYoungestToOldest = map.get(youngest).get(oldest);

Group sales by buyers and sellers


(lambdaj version)
Group<Sale> group = group(sales, by(on(Sale.class).getBuyer()),by(on(Sale.class).getSeller())); Person youngest = selectMin(persons, on(Person.class).getAge()); Person oldest = selectMax(persons, on(Person.class).getAge()); Sale sale = group.findGroup(youngest).find(oldest).get(0);

Find most bought car


(iterative version)
Map<Car, Integer> carsBought = new HashMap<Car, Integer>(); for (Sale sale : sales) { Car car = sale.getCar(); Integer boughtTimes = carsBought.get(car); carsBought.put(car, boughtTimes == null ? 1 : boughtTimes+1); } Car mostBoughtCarIterative = null; int boughtTimesIterative = 0; for (Entry<Car, Integer> entry : carsBought.entrySet()) { if (entry.getValue() > boughtTimesIterative) { mostBoughtCarIterative = entry.getKey(); boughtTimesIterative = entry.getValue(); } }

Find most bought car


(lambdaj version)
Group<Sale> group = selectMax( group(sales, by(on(Sale.class).getCar())).subgroups(), on(Group.class).getSize()); Car mostBoughtCar = group.findAll().get(0).getCar(); int boughtTimes = group.getSize();

Performance analysis
Minimum, maximum and average duration in milliseconds of 10 runs of 100,000 iterations of the former examples
PrintAllBrands FindAllSalesOfAFerrari FindAllBuysOfYoungestPerson FindMostCostlySaleValue SumCostsWhereBothActorsAreAMale AgeOfYoungestBuyerForMoreThan50K SortSalesByCost ExtractCarsOriginalCost IndexCarsByBrand GroupSalesByBuyersAndSellers FindMostBoughtCar min 656 688 13.485 531 843 12.890 3.250 328 469 23.657 8.296 iterative max 1.016 766 14.969 547 875 13.063 3.500 344 500 24.079 8.484 avg 769 761 14.757 544 859 12.965 3.421 333 485 23.835 8.395 min 5.812 6.172 18.953 3.546 10.828 24.860 21.953 1.218 1.484 36.375 28.421 lambdaj max 6.266 6.297 19.375 3.579 11.141 25.234 22.312 1.250 1.531 37.046 28.859 avg 5.905 6.214 19.124 3.564 11.021 25.089 22.080 1.228 1.511 36.686 28.614 ratio 7,679 8,166 1,296 6,551 12,830 1,935 6,454 3,688 3,115 1,539 3,408

Average ratio =

5,151

Known limitations
lambdaj cannot Lack of reified generics infer the actual type to be returned when a null or empty collection is passed to forEach()
List<Person> persons = new ArrayList<Person>(); forEach(persons).setLastName("Fusco"); Exception

Impossibility to proxy a final class the on() construct cannot register an invocation after a final Class is met
List<Person> sortedByNamePersons = sort(persons, on(Person.class).getName().toLowerCase()); Exception

Check out lambdaj at:

code.google.com/p/lambdaj
Thank you

Mario Fusco

mario@exmachina.ch

You might also like