Chapter: Java The Complete Reference - The Java Library - The Stream API

| Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail |

Collecting - Java Stream API

As the preceding examples have shown, it is possible (indeed, common) to obtain a stream from a collection.

Collecting

 

As the preceding examples have shown, it is possible (indeed, common) to obtain a stream from a collection. Sometimes it is desirable to obtain the opposite: to obtain a collection from a stream. To perform such an action, the stream API provides the collect( ) method. It has two forms. The one we will use first is shown here:

 

<R, A> R collect(Collector<? super T, A, R> collectorFunc)

 

Here, R specifies the type of the result, and T specifies the element type of the invoking stream. The internal accumulated type is specified by A. The collectorFunc specifies how the collection process works. The collect( ) method is a terminal operation.

The Collector interface is declared in java.util.stream, as shown here: interface Collector<T, A, R>

 

T, A, and R have the same meanings as just described. Collector specifies several methods, but for the purposes of this chapter, we won’t need to implement them. Instead, we will use two of the predefined collectors that are provided by the Collectors class, which is packaged in java.util.stream.

The Collectors class defines a number of static collector methods that you can use as-is. The two we will use are toList( ) and toSet( ), shown here:

 

static <T> Collector<T, ?, List<T>> toList( ) static <T> Collector<T, ?, Set<T>> toSet( )

 

The toList( ) method returns a collector that can be used to collect elements into a List. The toSet( ) method returns a collector that can be used to collect elements into a Set. For example, to collect elements into a List, you can call collect( ) like this:

collect(Collectors.toList())

 

The following program puts the preceding discussion into action. It reworks the example in the previous section so that it collects the names and phone numbers into a List and a Set.

 

 

// Use collect() to create a List and a Set from a stream.

 

import java.util.*;

 

import java.util.stream.*;

 

class NamePhoneEmail { String name;

 

String phonenum; String email;

 

NamePhoneEmail(String n, String p, String e) { name = n;

 

phonenum = p; email = e;

 

}

 

}

 

class NamePhone { String name; String phonenum;

 

NamePhone(String n, String p) { name = n;

 

phonenum = p;

 

}

 

}

 

class StreamDemo7 {

 

public static void main(String[] args) {

 

// A list of names, phone numbers, and e-mail addresses.

ArrayList<NamePhoneEmail> myList = new ArrayList<>( );

 

myList.add(new NamePhoneEmail("Larry", "555-5555", "Larry@HerbSchildt.com"));

 

myList.add(new NamePhoneEmail("James", "555-4444", "James@HerbSchildt.com"));

 

myList.add(new NamePhoneEmail("Mary", "555-3333", "Mary@HerbSchildt.com"));

 

     //Map just the names and phone numbers to a new stream.

     Stream<NamePhone> nameAndPhone = myList.stream().map(

 

     -> new NamePhone(a.name,a.phonenum)

 

);

//Use collect to create a List of the names and phone numbers. List<NamePhone> npList = nameAndPhone.collect(Collectors.toList());

 

System.out.println("Names and phone numbers in a List:"); for(NamePhone e : npList)

 

System.out.println(e.name + ": " + e.phonenum);

 

     //Obtain another mapping of the names and phone numbers.

     nameAndPhone = myList.stream().map(

 

     -> new NamePhone(a.name,a.phonenum)

 

);

 

     //Now, create a Set by use of collect().

 

Set<NamePhone> npSet = nameAndPhone.collect(Collectors.toSet());

 

System.out.println("\nNames and phone numbers in a Set:"); for(NamePhone e : npSet)

 

System.out.println(e.name + ": " + e.phonenum);

}

}

 

The output is shown here:

 

Names and phone numbers in a List:

 

Larry: 555-5555

 

James: 555-4444

 

Mary: 555-3333

 

Names and phone numbers in a Set:

 

James: 555-4444

 

Larry: 555-5555

 

Mary: 555-3333

 

In the program, the following line collects the name and phone numbers into a List by

 

using toList( ):

 

List<NamePhone> npList = nameAndPhone.collect(Collectors.toList());

 

After this line executes, the collection referred to by npList can be used like any other List collection. For example, it can be cycled through by using a for-each for loop, as shown in the next line:

 

for(NamePhone e : npList) System.out.println(e.name + ": " + e.phonenum);

 

The creation of a Set via collect(Collectors.toSet( )) works in the same way. The ability to move data from a collection to a stream, and then back to a collection again is a very powerful attribute of the stream API. It gives you the ability to operate on a collection through a stream, but then repackage it as a collection. Furthermore, the stream operations can, if appropriate, occur in parallel.

 

The version of collect( ) used by the previous example is quite convenient, and often the one you want, but there is a second version that gives you more control over the collection process. It is shown here:

 

<R> R collect(Supplier<R> target, BiConsumer<R, ? super T> accumulator, BiConsumer <R, R> combiner)

 

Here, target specifies how the object that holds the result is created. For example, to use a LinkedList as the result collection, you would specify its constructor. The accumulator function adds an element to the result and combiner combines two partial results. Thus, these functions work similarly to the way they do in reduce( ). For both, they must be stateless and non-interfering. They must also be associative.

 

Note that the target parameter is of type Supplier. It is a functional interface declared in java.util.function. It specifies only the get( ) method, which has no parameters and, in this case, returns an object of type R. Thus, as it relates to collect( ), get( ) returns a reference to a mutable storage object, such as a collection.

Note also that the types of accumulator and combiner are BiConsumer. This is a functional interface defined in java.util.function. It specifies the abstract method accept( ) that is shown here:

 

void accept(T obj, U obj2)

 

This method performs some type of operation on obj and obj2. As it relates to accumulator, obj specifies the target collection, and obj2 specifies the element to add to that collection. As it relates to combiner, obj and obj2 specify two collections that will be combined.

 

Using the version of collect( ) just described, you could use a LinkedList as the target in the preceding program, as shown here:

 

LinkedList<NamePhone> npList = nameAndPhone.collect(

 

() -> new LinkedList<>(),

 

(list, element) -> list.add(element), (listA,listB ) -> listA.addAll(listB));

 

Notice that the first argument to collect( ) is a lambda expression that returns a new LinkedList. The second argument uses the standard collection method add( ) to add an element to the list. The third element uses addAll( ) to combine two linked lists. As a point of interest, you can use any method defined by LinkedList to add an element to the list. For example, you could use addFirst( ) to add elements to the start of the list, as shown here:

 

(list, element) -> list.addFirst(element)

 

As you may have guessed, it is not always necessary to specify a lambda expression for the arguments to collect( ). Often, method and/or constructor references will suffice. For example, again assuming the preceding program, this statement creates a HashSet that contains all of the elements in the nameAndPhone stream:

 

HashSet<NamePhone> npSet = nameAndPhone.collect(HashSet::new, HashSet::add, HashSet::addAll);

 

Notice that the first argument specifies the HashSet constructor reference. The second and third specify method references to HashSet’s add( ) and addAll( ) methods.

One last point: In the language of the stream API, the collect( ) method performs what is called a mutable reduction. This is because the result of the reduction is a mutable (i.e., changeable) storage object, such as a collection.


Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail


Copyright © 2018-2020 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.