Streams are an update of the Java API which allows to manipulate collections in a declarative way, following a functional programming style, that this new Java release, and the following ones, aims at. As a matter of fact, starting with its 8th release, in addition of its object orientation and its imperative programming style, Java becomes a functional programming language as well, exactly like Scala.
This blog ticket tries to present and explain, in a simpler way than the Java specifications, the streams API and to illustrate it with examples taken from the real world.
There were many attempts to provide Java programmers with better libraries to manipulate collections. Among them, Guava is a popular one created by Google which brings things like multimaps and multisets. The Apache Common Collections library provides similar features. Finally, lambdaj, written by Mario Fusco, one of the authors of the book Modern Java in Action that I strongly recommend, provides many utilities to manipulate collections in a declarative way, inspired by the functional programming. Finally, Java 8 comes with its own official library for manipulating collections in a more declarative style.
In order to process Java collections in a traditional imperative style, a developer uses a so-called external iteration, for exemple a for-each statement. The streams library, by contrast, uses so-called internal iterations, meaning that it's the library itself which does the iteration and stores the result somewhere. This saves a lot of boiler-plate code to the developer who merely provides a function saying what's to be done, instead of saying how to do it, like in the case of the imperative style code. Let's look at an example:
List orders = new ArrayList<>();
for (Customer customer : customers)
orders.add(customer.getOrder());
The previous sequence of code is using an external iteration in order to build a list of orders. This is the traditional Java imperative programming style which uses the for-each construct. The same processing may be written using an internal iterator as follows:
List orders = customers.stream().map(Customer::getOrder).collect(toList());
Besides being more concise, the 2nd implementation doesn't tell how to build the list of orders, by iterating on customers, etc. It just tells to map a list of customers to the associated list of orders.
The streams API is quite complex and requires a lot of exercice before acquiring a good command of it. Hence I created this small project in order to provide a couple of hopefully useful examples. Given some customers and orders based collections, we are performing SQL like queries on them. Here is the domain we'll be working on:
public class Customer
{
private String firstName;
private String lastName;
private String street;
private String city;
private String state;
private String zip;
private String country;
...
)
public class Order
{
private Customer customer;
private LocalDate date;
private BigDecimal value;
...
)
private List orders = Arrays.asList (
new Order(new Customer("Nicolas", "Duminil", "26 Allée des Sapins",
"Soisy sous Motmorency", "Val d'Oise", "95230", "FR"),
LocalDate.now(), new BigDecimal(300)),
new Order(new Customer("Aleshia", "Tomkiewicz", "14 Taylor St",
"St. Stephens Ward", "Kent", "CT2 7PP", "UK"),
LocalDate.now(), new BigDecimal(1000)),
new Order(new Customer("Evan", "Zigomalas",
"5 Binney St", "Abbey Ward", "Buckinghamshire", "HP11 2AX", "UK"),
LocalDate.now(), new BigDecimal(400)),
new Order(new Customer("France", "Andrade", "8 Moor Place",
"East Southbourne and Tuckton W", "Bournemouth", "BH6 3BE", "UK"),
LocalDate.now(), new BigDecimal(710)),
new Order(new Customer("Eric", "Rampy", "9472 Lind St",
"Desborough", "Northamptonshire", "NN14 2GH", "UK"),
LocalDate.now(), new BigDecimal(700)),
new Order(new Customer("Corinne", "Jaret", "2150 Morley St",
"Dee Ward", "Dumfries and Galloway", "DG8 7DE", "UK"),
LocalDate.now(), new BigDecimal(300))
);
Given the domain above, we need to implement the following queries:
- Find all orders for a given year sorted by order value
- What are all the unique cities where customers are based ?
- Find all customers based in a given city and sort them by their last name
- Return a string of all customers' last names sorted alphabetically
- Are there any customers in a given city ?
- Return the values of all orders associated to customers based in a given city
- What's the highest value of all the orders ?
- Find the order with the smallest value
The solutions are in the previously mentioned GitHub project. Good luck !