I have been in the software development industry since 2010, working on enterprise product development using ADF. I am usually keen on learning about software design and emerging technologies. You can find me hanging around in the JavaRanch Forums where I am one of the moderators. Apart from Java, I am fascinated by the ease of use and simplicity of Ruby and Rails. Mohamed is a DZone MVB and is not an employee of DZone and has posted 53 posts at DZone. You can read more from them at their website. View Full User Profile

Extracting the Elements of the Java Collection- The Java 8 Way

03.23.2013
| 11648 views |
  • submit to reddit

We all have extensively used Collection classes like List, Map and their derived versions. And each time we used them we had to iterate through them to either find some element or update the elements or find different elements matching some condition. Consider a List of Person as shown below:

List<Person> personList = new ArrayList<>();
personList.add(new Person("Virat", "Kohli",22));
personList.add(new Person("Arun", "Kumar",25));
personList.add(new Person("Rajesh", "Mohan", 32));
personList.add(new Person("Rahul", "Dravid", 35));

To find out all the Person instances with age greater than 30, we would do:

List<Person> olderThan30OldWay = new ArrayList<>();
for ( Person p : personList){
  if ( p.age >= 30){
    olderThan30OldWay.add(p);
  }
}
System.out.println(olderThan30OldWay);

and this gives me the output as:

[Rajesh Mohan, 32, Rahul Dravid, 35]

The code is easy to write, but is it not a bit more verbose, especially the iteration part? Why would we have to iterate? Would it not be cool if there was an API which would iterate the contents and give us the end result i.e we give the source List and use a series of method calls to get us the result List we are looking for? Yes, this is possible in other languages like Scala, Groovy which support passing closures and also support internal iteration. But is there a solution for Java developers? Yes, this exact problem is being solved by introducing support for Lambda Expressions(closures) and a enhanced Collection API to leverage the lambda expression support. The sad news is that it’s going to be part of Java 8 and will take some time to be into mainstream development.

Leveraging the Java 8 enhancements to the above scenario

As I said before the Collections API is being enhanced to support the use of Lambda Expression and more about it can be read here. Instead of adding all the new APIs to the Collection class the JDK team created a new concept called “Stream” and added most of the APIs in that class. “Stream” is a sequence of elements obtained from the Collection from which it is created. To read more about the origin of Stream class please refer to this document.

To implement the example I started with using the enhancements in Java 8 we would be using few new APIs namely: stream(), filter(), collect(), Collectors.toCollection().
stream(): Uses the collection on which this API is called to create an instance of Stream class.
filter():This method accepts a lambda expression which takes in one parameter and returns a boolean value. This lambda expression is written as a replacement for implementing the Predicate class.
collect(): There are 2 overloaded versions of this method. The one I am using here takes an instance of Collector. This method takes the contents of the stream and constructs another collection. This construction logic is defined by the Collector.
Collectors.toCollection(): Collectors is a factory for Collector. And the toCollection() takes a Lambda expression/Method reference which should return a new instance of any derivatives of Collection class.

With brief introduction to the APIs used, let me show the code which is equivalent to the first code sample:

List<Person> olderThan30 = 
    //Create a Stream from the personList
    personList.stream().
    //filter the element to select only those with age >= 30
    filter(p -> p.age >= 30).
    //put those filtered elements into a new List.
    collect(Collectors.toCollection(() -> new ArrayList<Person>()));
System.out.println(olderThan30);

The above code uses both Internal iteration and lambda expressions to make it intuitive, concise and soothing to the eye.
If you are not familiar with the idea of Lambda Expressions, check out my previous entry which covers in brief about Lambda expressions.



 

Published at DZone with permission of Mohamed Sanaulla, author and DZone MVB. (source)

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

Comments

Manrico Corazzi replied on Sat, 2013/03/23 - 3:00pm

I do think your definitions of " intuitive, concise and soothing to the eye" and mine are pretty different.

Yours is a very interesting example, and though I think it's pretty neat you started with just a bunch of super-clear lines of code and replaced them with actually MORE code which my Java 7.x  brain fail to grasp at a glance.

Nonetheless this is a good starting point, I think Java is finally getting there.

I'll stick with Groovy for this kind of list manipulation feats, for the time being.

Peter Henderson replied on Sun, 2013/03/24 - 5:36am

wow. Looks like Java 8 lambdas are a car crash. Compare to Scala

case class Person(first: String, last: String, age: Int)

val personList = List(
  Person("Virat", "Kohli",22),
  Person("Arun", "Kumar",25),
  Person("Rajesh", "Mohan", 32),
  Person("Rahul", "Dravid", 35)
  )

val olderThan30 = personList.filter(_.age >= 30)

println(olderThan30)

Mohamed Sanaulla replied on Sun, 2013/03/24 - 9:02am in response to: Manrico Corazzi

Really cannot compare with the likes of Groovy and Scala as those concepts were part of the language since their inception. But adding such a huge functionality in an already well established language is a really tasking and risky move. But hats off to the engineers who worked on it to give Java developers something to atleast fancy about. 

Mohamed Sanaulla replied on Sun, 2013/03/24 - 9:14am in response to: Peter Henderson

The reply for this would be same as the one posted here. New languages being introduced learn from the past languages and keep evolving. But something coming into Java would be useful for enterprises who have their investment in Java. And convincing such enterprises to use Scala is not always an easy. For that matter enterprises picking Java 8 for immediate development is another thing that is not possible.

Oleksandr Alesinskyy replied on Wed, 2013/03/27 - 5:16am in response to: Mohamed Sanaulla

Any functionality added in a so odd way becomes (virtually) useless. That means it becomes as good as non-existent.

It is the second time (at least - the first was an introduction of generics) a development of Java steps on the same rakes - instead of to freeze legacy things (collections framework)  and to create a clean and proper replacement the ugly fix is created in sake of "backward compatibility", a clear benefit in a very short run and a constant pain afterwards.

Mohamed Sanaulla replied on Wed, 2013/03/27 - 6:05am in response to: Oleksandr Alesinskyy

I think this enhancement can be safely used alongside the legacy code/collections framework. Those who have picked the new Lambda style and want to leverage parallelism use the new Stream APIs and the others stick to the old collections way. I dont see anything odd in here. Can you help us to point the oddness?

List<Person> olderThan30 =
personList.stream().
filter(p -> p.age >= 30).
collect(Collectors.toCollection(ArrayList::new));

I just reformatted the code removing the comments and also used Method reference in place of the lambda expression for brevity.

Creating a new framework along side this huge feature would ask for more time and I think it was important for the language to have this support first rather than a new collections framework. I remember reading in the State of Collections API or some related doc by Brian Goetz that a new collections framework is in their plan of action.

Oleksandr Alesinskyy replied on Wed, 2013/03/27 - 7:51am in response to: Mohamed Sanaulla

To see the oddity compare it with a Scala version from the Peter Henderson's post above.
A lot of syntactical noise just to ensure a mix-n-match style. To be more precise - both stream() and, especially, collect() invocations are nothing more then noise.




Mohamed Sanaulla replied on Wed, 2013/03/27 - 8:29am in response to: Oleksandr Alesinskyy

Agree to your point, but this was designed in Scala language right from its the creation, but not like that in Java

Oleksandr Alesinskyy replied on Wed, 2013/03/27 - 9:41am in response to: Mohamed Sanaulla

But it was quite possible to bring it into Java in a right way (probably, sacrificing its compatibility with the old collection framework, and not in the current ugly way.

Just compare how generics were added to Java (ugly) and .Net (neat and nice, while ant the cost of reimplementing a .Net analogue of the Java Collections framework).



Per Lundholm replied on Sun, 2013/06/16 - 5:47am in response to: Peter Henderson

Oh, you can come down to more compact code in Java too.

       Stream<Person> personList = Stream.of(
                new Person("Virat", "Kohli", 22),
                new Person("Arun", "Kumar", 25),
                new Person("Rajesh", "Mohan", 32),
                new Person("Rahul", "Dravid", 35)
       );
       Stream<Person> filteredStream = personList.filter(p -> p.age >= 30);
       filteredStream.forEach(System.out::println);

Comment viewing options

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