This article focuses on how to use the Stream API. Many source code examples and explanations are provided so that even those who are new to the Stream API can understand it. It also introduces applied usage such as Strategy pattern using Stream API.
――Because the processing is done like words, readability is improved. --Clear input and output make it easier to write test code --Bugs are hard to hide because it does not cause state changes --Combining functions does not affect each other --Supports parallel processing
It can be used in places where loop processing such as for statements is performed most often.
The basic flow is to create a Stream instance, perform as many processes as you want (intermediate operation), and finally bring it to the desired state (end point operation).
Let's compare ** if you did not use the Stream API ** and ** if you did use ** to imagine this process.
The code written below shows the source code that converts the character string stored in the List to uppercase and extracts and displays only the 4-character character string.
//Code written without using Stream API
List<String> list2 = Arrays.asList("hoge", "foo", "bar","hoga");
List<String> stream2 = new ArrayList<String>();
for(String s :list2){
String sUpperCase = s.toUpperCase();
if(sUpperCase.length() == 4){
stream2.add(sUpperCase);
}
}
for(int i = 0; i < stream2.size(); i++){
System.out.println(stream2.get(i));
}
/*
The result is as follows
HOGE
HOGA
*/
//When using Stream API
List<String> list1 = Arrays.asList("hoge", "foo", "bar","hoga");
List<String> stream1 = list1.stream()
.map(String::toUpperCase)
.filter(s -> s.length() == 4)
.collect(Collectors.toList());
stream1.forEach(System.out::println);
/*
The result is as follows
HOGE
HOGA
*/
Using the Stream API like this is better readability because you only have to write what you want to do declaratively. (There is an advantage because there is no other state change and unnecessary processing does not run due to processing delay)
Now that you have an image of how to use the Stream API, I will explain in detail how to use it.
I will explain the flow of Stream separately as follows. ** 1. Stream instance creation ** ** 2. Intermediate operation ** ** 3. End point operation **
There are many ways to create a Stream instance, but here are four that you might use most often.
Class / interface | Method | Overview |
---|---|---|
Collection |
stream() | Stream instance generation from a class that inherits the Collection interface |
Arrays | stream (T[] array) | Create a Stream instance from an array |
Stream | of(T… values) | Create a Stream instance based on the direct value |
Stream | iterate(T seed, UnaryOperator f) | Create an ordered infinite Stream instance |
How to write source code
// Collection#stream()
List<String> list = Arrays.asList("hoge", "foo", "bar");
Stream<String> stream = list.stream();
// Arrays#stream (T[] array)
String[] array = {"hoge", "foo", "bar"};
Stream<String> stream2 = Arrays.stream(array);
// Stream#of (T[] array)
Stream<String> stream3 = Stream.of("hoge", "foo", "bar");
// Stream#iterate (T seed, UnaryOperator f)
Stream<Integer> stream4 = Stream.iterate(2, x -> x * 2);
Intermediate operations are roughly divided into three processing methods.
--Narrow down the elements --Processing of elements --Sort elements
** 1. I think you understand how to create a Stream instance **, so I will narrow down the intermediate operations for the generated Stream instance.
Since it is narrowed down, the number of inputs and outputs will change.
Method | Overview |
---|---|
filter(Predicate<? super T> predicate) | Stream that narrows down only the elements whose boolean judgment defined in Predicate is true |
limit(long maxSize) | Returns a Stream of elements from the beginning of the element to maxSize |
skip(long maxSize) | Returns a Stream that skips elements from the beginning of the element to maxSize |
distinct() | Compare elements with equals method and return Stream excluding duplicates |
How to write source code
List<String> list = Arrays.asList("hoge", "foo", "bar","hoga","hoga");
//I used filter to extract only a 4-character string
System.out.println("****↓ filter****");
list.stream()
.filter(s -> s.length() == 4)
.forEach(System.out::println);
/*
Output result
hoge
hoga
hoga
*/
List<String> list = Arrays.asList("hoge", "foo", "bar","hoga","hoga");
//Extracted up to 3 strings from the first character using limit
list.stream()
.limit(3)
.forEach(System.out::println);
/*
Output result
hoge
foo
bar
*/
List<String> list = Arrays.asList("hoge", "foo", "bar","hoga","hoga");
//I used skip to skip the element string from the first character
list.stream()
.skip(1)
.limit(2)
.forEach(System.out::println);
/*
Output result
foo
bar
*/
List<String> list = Arrays.asList("hoge", "foo", "bar","hoga","hoga");
//Removed duplicate strings using distinct
list.stream()
.distinct()
.forEach(System.out::println);
/*
Output result
hoge
foo
bar
hoga
*/
The number of inputs and outputs does not change because it is only processed. For example, you can change from lowercase to uppercase, or from a string to the number of characters in a string.
Method | Overview |
---|---|
map(Function<? super T, ? extends R> function) | Return R from T received by function as an argument, and Stream generated with that R as an element |
How to write source code
List<String> list = Arrays.asList("hoge", "foo", "bar","hoga");
list.stream()
.map(String::length)
.forEach(System.out::println);
/*
Output result
4
3
3
4
*/
Method | Overview |
---|---|
sorted(Comparator<? super T> comparator) | Stream compared and sorted by Comparator |
How to write source code
List<Person> people = Arrays.asList(
new Person("Suzuki",24),
new Person("Yamada",53),
new Person("Henri Fortin",9),
new Person("Yokouchi",39));
//When sorted by age
people.stream()
.sorted(comparing(Person::getAge))
.forEach(s -> System.out.println(s.toString()));
/*
Output result
Henri Fortin:9
Suzuki:24
Yokouchi:39
Yamada:53
*/
//Sort by name.reversed()When using in descending order
people.stream()
.sorted(comparing(Person::getName).reversed())
.forEach(s -> System.out.println(s.toString()));
}
/*
Output result
Suzuki:24
Yokouchi:39
Yamada:53
Henri Fortin:9
*/
//Person class used above
public class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public String toString(){
return name + ":" + age;
}
}
The end point operation is the last operation that creates a Stream instance and finishes intermediate operations.
Method | Return type | Contents |
---|---|---|
forEach(Consumer<? super T> consumer) | void | Consumer receives each element of Stream as an argument and processes it. When used in parallel processing, the order is not guaranteed even if the original data is an aggregate such as List. |
forEachOrdered(Consumer<? super T> consumer) | void | If each element of Stream guarantees the order, the consumer receives each element as an argument in order and processes it. |
toArray() | Object[] | Returns the elements of Stream as an array of Objects |
reduce(T unit, BinaryOperator acc) | T | reduction(Convolution).. Returns the result of combining elements with the cumulative function acc for the identity element unit. |
collect(Supplier factory, BiConsumer acc, BiConsumer combiner) | Result container | Variable reduction. Variable container generated by factory(For example ArrayList)On the other hand, add an element with acc and combine each container with combiner. |
min(Comparator<? super T> comparator) | Optional |
Returns the smallest of the elements. Use the argument comparator to compare the size. Returns an empty Optional if there is no element |
max(Comparator<? super T> comparator) | Optional |
Returns the largest of the elements. Use the argument comparator to compare the size. Returns an empty Optional if there is no element |
count() | long | Returns the number of elements that Stream has |
anyMatch(Predicate<? super T> predicate) | boolean | If all the elements of Stream return True in the judgment of predicate, True is returned as the return value. |
allMatch(Predicate<? super T> predicate) | boolean | If all the elements of Stream return True in the judgment of predicate, True is returned as the return value. |
noneMatch(Predicate<? super T> predicate) | boolean | If none of the Stream elements return True on the predicate check, return True as the return value. |
findFirst() | Optional |
Returns the first element of the elements. Returns an empty Optional if there is no element |
findAny() | Optional |
Returns one element of the elements. Returns an empty Optional if there is no element |
sum() | int / long /double | Returns the sum of the elements that Stream has. The return value will be what the Stream represents. Returns 0 if there are no elements |
average() | OptionalDouble | Returns the average value. If there is no element, Empty OptionalDouble is returned. If it is not divisible, it is rounded to a value that can be represented by a double value. |
How to write source code Since the amount is large this time, I wrote an example by extracting about 3 appropriately. There is an optional return value, which will be explained later.
List<Person> people = Arrays.asList(
new Person("Suzuki",24),
new Person("Yamada",53),
new Person("Henri Fortin",9),
new Person("Yokouchi",39));
//Sort by age and store in List
List<Person> sortedPeople = people.stream()
.sorted(comparing(Person::getAge))
.collect(Collectors.toList());
System.out.println(sortedPeople);
/*
Output result
[Henri Fortin:9,Suzuki:24,Yokouchi:39,Yamada:53]
*/
//Sort by age in descending order and get the first Person
Optional<Person> firstPerson = people.stream()
.sorted(comparing(Person::getAge).reversed())
.findFirst();
System.out.println(firstPerson);
/*
Output result
Optional[Yamada:53]
*/
//True if all Persons are 10 or older, false otherwise
boolean ss = people.stream()
.allMatch(p -> p.getAge() > 10);
System.out.println(ss);
/*
Output result
false
*/
A container object that can be stored with or without non-null values. There is no need to perform nullpo processing such as if (xxx == null). Please read it as it is organized in an easy-to-understand document. https://docs.oracle.com/javase/jp/8/docs/api/java/util/Optional.html
This is an example using the ifPresent (Consumer <? Super T> consumer) method. This program outputs the first value found when the last name has 3 or more characters.
//If Optional is not used
List<String> people = Arrays.asList("Yamada","Tanaka","Suzuki");
String firsPerson = null;
for(String s : people){
if(s.length() == 3){
firsPerson = s;
break;
}
}
//null check
if(firsPerson != null){
System.out.println(firsPerson);
}
//Use Optional and execute only when the value exists
Optional<String> firstPerson = Stream.of("Yamada","Tanaka","Suzuki")
.filter(s -> s.length() == 3)
.findFirst();
firstPerson.ifPresent(System.out::println);
If the value exists, it calls the specified consumer with that value, otherwise it does nothing.
Predicate
Returns the boolean of the processing result. In addition, processing can be connected, so if there is processing that connects a large number of if statements, It can be used effectively.
Implement the process of receiving a T type value as an argument and returning a boolean value. The value of boolean is returned by giving the argument T to the test method.
Predicate<String> predicate = s -> s.length() == 5;
System.out.println(predicate.test("tarou"));
Function
Represents a function that takes a single argument and produces a result.
Implement the process of receiving a T-type argument and returning an R-type value. It can be executed by giving the argument T to the apply method.
Function<String,String> toUpper = s -> s.toUpperCase();
System.out.println(toUpper.apply("tarou"));
Consumer
It receives a T type value as an argument and implements processing without a return value. It can be executed by giving the argument T to the accept method.
Consumer<String> consumer = string -> System.out.println("Cunsumer : " + string);
consumer.accept("tarou");
comming soon
comming soon
Recommended Posts