Java Lambda expressions from set to stream

  • 2020-06-12 09:16:38
  • OfStack

From the set to the stream

Now that we are iterating over a set of 1 in code, we want to define an Contact class to represent contacts and wrap all the contact names of String types in ContactList into the Contact class:


List<Contact> contacts = new ArrayList<>();
contactList.forEach(new Consumer<String>() {
 @Override
 public void accept(String s) {
  Contact contact = new Contact();
  contact.setName(s);
  contacts.add(contact);
 }
});

Next, we hope to screen out all the contacts that can still get through and put them into a valid contact set:


List<Contact> validContacts = new ArrayList<>();
contacts.forEach(new Consumer<Contact>() {
 @Override
 public void accept(Contact c) {
  if (c.call())
   validContacts.add(c);
 }
});
System.out.println(validContacts.size());

It can be seen that in the first operation, the data of String type was converted to Contact, and in the second operation, the method of call() was called for every Contact to screen out the contact whose return result was true and collect it into another set. Finally, we counted the number of contacts that could still get through.

In this process, the operation behavior is completely enclosed within the collections without introducing any external variables.

From the beginning to the end of the processing, the object moves as an ordered sequence in the operation, which is the characteristic of the flow, namely "data in motion".

A true stream is very different from a collection, representing only an "optional sequence of ordered values" without "providing any storage for these values", which is why Stream in Java8-ES26en is defined as an interface rather than a class.


public interface Stream<T> extends BaseStream<T, Stream<T>> {}

Stream < T > Is the stream of the object, while DoubleStream, LongStream, and IntStream are the three basic types of streams: double, long, and int.

Now let's rewrite the first mapping from String to Contact as a stream:


Stream<Contact> contactStream = contactList.stream().map(s -> new Contact().setName(s));

stream() takes the pipe from the source, indicating the beginning of the flow.

map() receives a stream from a pipe and performs some transformation on it. In this case, we map String from the pipe to the Contact class, and from there, the String pipe becomes the Contact pipe.

We can split the previous section of code into:


Stream<String> stringStream = contactList.stream();
Stream<Contact> contactStream1 = stringStream.map(s -> new Contact().setName(s));

After the basic understanding of the flow operation, we now have 1 gas and directly use the flow to get the final result:


long validContactCounter = 
 contactList.stream()
  .map(s -> new Contact().setName(s))
  .filter(c -> c.call())
  .count();

As you can see, we are able to perform a wealth of operations, filtering, counting, lookup, and so on, not listed here.

summary

Working with data in streams can simplify code while highlighting the operations to be performed, which, of course, may seem confusing at first glance.

We sacrificed a little readability, but in exchange, we got twice the performance of the corresponding circular version in this sequential flow operation.

Similarly, performing flow operations in parallel can have remarkable effects on large data sets.

Relevant code in this section:

(Contact java)


import java.util.Random;
public class Contact {
 private String name;
 private long number;
 private Random random;
 public Contact() {
  random = new Random();
 }
 public String getName() {
  return name;
 }
 public Contact setName(String name) {
  this.name = name;
  return this;
 }
 public long getNumber() {
  return number;
 }
 public Contact setNumber(long number) {
  this.number = number;
  return this;
 }
 public boolean call() {
  return random.nextBoolean();
 }
}
 (Operational) 

List<Contact> contacts = new ArrayList<>();
contactList.forEach(new Consumer<String>() {
 @Override
 public void accept(String s) {
  Contact contact = new Contact();
  contact.setName(s);
  contacts.add(contact);
 }
});
List<Contact> validContacts = new ArrayList<>();
contacts.forEach(new Consumer<Contact>() {
 @Override
 public void accept(Contact contact) {
  if (contact.call())
   validContacts.add(contact);
 }
});
System.out.println(validContacts.size());
//--- Stream is coming ---//
Stream<Contact> contactStream = contactList.stream().map(s -> new Contact().setName(s));
//--- Break this code ---//
Stream<String> stringStream = contactList.stream();
Stream<Contact> contactStream1 = stringStream.map(s -> new Contact().setName(s));
//--- All in one ---//
long validContactCounter = 
 contactList.stream()
  .map(s -> new Contact().setName(s))
  .filter(c -> c.call())
  .count();
System.out.println(validContactCounter);

And operation results:

3
3


Related articles: