[JAVA] I tried to summarize the Stream API

What is Stream API?

As the name suggests, Stream API is a group of APIs for processing flowing data and events called Stream. Added in Java SE 8 Stream is generated based on a data set such as List or Map, and the result is obtained by executing 0 or more intermediate operations and 1 termination operation.

How to use Stream API

After getting a rough overview, let's actually look at the source and compare the processing using stream with the processing not using it. As a sample, I will write the code that processes List and processes it one by one.

No stream


		List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
		for (Integer i : integerList) {
		    if (i % 2 == 0) {
		        System.out.println(i);
		    }

stream description


		List<Integer> integerList = Arrays.asList(1,2,3,4);
		integerList.stream()
		           .filter(i -> i % 2 ==0)
		           .forEach(i -> System.out.println(i));

Output result


2
4

It is a code that turns a simple List with a for statement, makes a judgment with an if statement, and outputs it. The part to be processed by turning with the for statement can be replaced with stream, and the if statement directly narrows down the internal elements with filter and outputs the remaining elements with forEach.

The former may be easier to see for those who are accustomed to the environment before Java8, but the latter will become easier to use and understand as you become accustomed to writing streams. In stream, the methods are divided into units that let you know what to do, and if you suppress the usage of each method, it is a description method that you can intuitively grasp the processing content.

The Stream API is used by continuously performing the processes of "creating", "manipulating", and "combining" a Stream for a data structure.

Generate operation

There are roughly the following methods for creating a stream.

java.util.stream.Stream#of(T...) : Stream java.util.stream.Stream.Builder#build() : Stream java.util.Collection#stream() : Stream java.util.Arrays#stream(T[]) : Stream java.io.BufferedReader#lines() : Stream java.nio.Files#lines(Path) : Stream java.nio.Files#lines(Path, Charset) : Stream java.util.regex.Pattern#splitAsStream(CharSequence) : Stream java.lang.CharSequence#chars() : IntStream java.lang.CharSequence#charPoints() : IntStream

This time, I will introduce two cases, one is to make it from the Collection interface such as List and Set, which are often used, and the other is to make it from an array. To create a Stream from a List or Set, use the stream () method. If you want to create a Stream from an array, use the Arrays.stream () method.

python


		//List stream processing
		List<Integer> numbers = List.of(3, 1, -4, 1, -5, 9, -2, 6, 5, 3, 5);
		Stream<Integer> stream = numbers.stream();
		//Steram processing of arrays
		int[] array = {3, 1, -4, 1, -5, 9, -2, 6, 5, 3, 5};
		IntStream stream = Arrays.stream(array);

If you want to create a Stream from Map, there is no API to create Stream directly from Map, so create Stream from Set obtained by using the entrySet method of Map.

		Map<String, Integer> map = Map.of("key1", 3, "ke2", 1, "key3", -4, "key4", 1);
		Stream<Entry<String, Integer>> stream = map.entrySet().stream();

You can use the Files.line method to read a text file line by line and make it a Stream of strings.

		Stream<String> lines = Files.lines(Path.of("/tmp/test.txt"));

Intermediate operation

The process performed on Stream is called an intermediate operation. The intermediate operations have the following methods.

peek(Consumer<? super T> action) skip(long maxSize) limit(long maxSize) filter(Predicate<? super T> predicate) distinct() sorted() / sorted(Comparator<? super T> comparator) map(Function<? super T,? extends R> mapper) flatMap(Function<? super T,? extends Stream<? extends R>> mapper) parallel() sequencial() unordered()

This time, the methods I often use I will focus on sorted, map, and filter.

1.sorted() sorted () returns a Stream that sorts the elements of the Stream. There are two overloads for sorted (), with and without arguments.

Sorted () with no arguments simply sorts the elements in ascending natural order. (However, the element class must be Comaprable.

If you want to sort in "unnatural" order or descending order, pass a custom Comparator as a function to soreted () with arguments. (If you give it in lambda, it is simpler to use Comparator # comparing ().)

		List<String> array = Arrays.asList("a","1","-2","Ah","A","123");

		System.out.println("--No arguments--");
		//No arguments
		array.stream()
			.sorted()  //Natural order ascending order
			.forEach(System.out::println);

		System.out.println("--Comparator--");
	    // Comparator
		array.stream()
	            .sorted(Comparator.reverseOrder()) //Natural order descending order
	            .forEach(System.out::println);

		System.out.println("--Lambda expression--");
	    //Lambda expression
		array.stream()
	            .sorted((l, r) -> l.length() - r.length()) //String length descending order
	            .forEach(System.out::println);

		System.out.println("--Method reference--");
	    //Method reference(Comparable)
		array.stream()
	            .sorted(String::compareToIgnoreCase) //Ignore case
	            .forEach(System.out::println);

		System.out.println("--Function object--");
	    //Function object
		array.stream()
	            .sorted(String.CASE_INSENSITIVE_ORDER) //Ignore case
	            .forEach(System.out::println);

	    // java.util.List`Also 1.At 8`sort()`Is added with the default method.
	    //This is more economical if the purpose is to change the element order of the List itself.
		System.out.println("--Sort by List--");
	    //Specify Comparator
		array.sort(Comparator.reverseOrder());
	    array.stream()
	    	.forEach(System.out::println);

Output result


--No arguments--
-2
1
123
A
a
Ah
--Comparator--
Ah
a
A
123
1
-2
--Lambda expression--
a
1
Ah
A
-2
123
--Method reference--
-2
1
123
a
A
Ah
--Function object--
-2
1
123
a
A
Ah
--Sort by List--
Ah
a
A
123
1
-2

2.filter()/distinct()

filter () and distinct () thin out the elements of the Stream by their contents.

filter () is an intermediate operation that narrows down the elements according to the conditions, and gives a predicate function (Predicate) for judgment as an argument. As long as the predicate looks only at the value of the element, filter () is a stateless intermediate operation. distinct () is an intermediate operation that eliminates duplicate elements.

        System.out.println("--filter--");
		List array = Arrays.asList("a.txt","b.com","c.new");
        array.stream()
        .filter(s -> ((String) s).endsWith(".txt")) //Lambda expression
        .forEach(System.out::println);

        System.out.println("--distinct--");
        String data = "aaa aaa bbb aaa ccc bbb ccc ccc";
        String uniq = Stream.of(data.split("\\s"))
                .peek(s -> System.out.print("\n" +  s))
                .distinct()
                .peek(s -> System.out.print("\t" + s))
                .collect(Collectors.joining(","));
        System.out.println();
        System.out.println(uniq);

Execution result


--filter--
a.txt
--distinct--

aaa	aaa
aaa
bbb	bbb
aaa
ccc	ccc
bbb
ccc
ccc
aaa,bbb,ccc

3.map()

map () returns a Stream whose elements have been transformed by the given function. You may change the type of the Stream element in the return type of the function.

	    //Preparation of function (capitalize first letter)
	    Function<String, String> capitalize = (String s) -> {
	        return String.format("%c%s",
	                Character.toTitleCase(s.charAt(0)),
	                s.substring(1));
	    };

	    List<String> words = Arrays.asList("aasDfag");
	    words = words.stream()
	    		.peek(System.out::println)
	            .map(s -> s.trim())        //Lambda expression
	            .filter(s -> !s.isEmpty())
	            .map(String::toLowerCase)  //Method reference
	            .peek(System.out::println)
	            .map(capitalize)           //Function object
	            .collect(Collectors.toList());
	    System.out.println(words);

Execution result


aasDfag
aasdfag
[Aasdfag]

Termination operation

The termination operation can be roughly divided into four processes.

--Search --Aggregation

This time, I will introduce the methods in four parts.

1. Search

1.1. findFirst()/findAny()

findFirst () returns the first element as Optional. findAny () returns the first element as Optional. Optional may be empty.

	    String[] words = {"aaaaaa", "bbbbbb", "cccccc"};

	    List<String> list = Arrays.asList(words);
	    Optional<String> first = list.stream().findFirst();
	    first.ifPresent(s -> {
	        System.out.println(s);  // "aaaaaa"
	    });

	    Set<String> set = new HashSet<>(list);
	    Optional<String> any = set.stream().findAny();
	    any.ifPresent(s -> {
	        System.out.println(s);  // "cccccc"
	    });

1.2. allMatch() / anyMatch() / noneMatch()

allMatch () / anyMatch () / noneMatch () searches the Stream result element based on the given predicate function (Predicate) and determines the existence status of the matching element.

	    List<String> list = Arrays.asList("a","asdf","");
	    boolean ok;

	    //Lambda expression
	    ok = list.stream()
	            .allMatch(s -> s != null && !s.isEmpty()); //Does not contain null and empty string
	    System.out.println(ok);
	    //Method reference
	    ok = list.stream()
	            .allMatch(Objects::nonNull);    //Does not contain null
	    System.out.println(ok);
	    //Predicate function
	    ok = list.stream()
	            .noneMatch(Predicate.isEqual("")); //Nullable and does not contain the empty string
	    System.out.println(ok);

Execution result


false
true
false

2. Aggregation

2.1. count() / min() / max()

count () literally counts the number of Stream elements. Since it really counts, it takes some processing time.

    //Number of lines in the text file
    int lc = (int) Files.lines(Paths.get("text.txt")).count();
    //Number of words in the text
    int wc = (int) Pattern.compile("\\W+").splitAsStream(text).count();
    //Count the difference in letters
    int vc = (int) text.codePoints().distinct().count();

Pass a comparison function (Comparator) to min () / max () to get the maximum and minimum values of the elements. The return value is Optional , which is empty if there is no element. Stream is always loaded to the end.

    List<String> list = ... ;
    Optional<String> min;

    // Comparator
    min = list.stream()
            .min(Comparator.naturalOrder()); //The smallest string in dictionary order
    //Method reference
    min = list.stream()
            .min(String::compareToIgnoreCase); //Case insensitive
    //Lambda expression
    min = list.stream()
            .min((l, r) -> l.length() - r.length()); //Shortest string
    // Comparable
    min = list.stream()
            .min(Comparator.comparing(s -> s.toUpperCase())); //Case insensitive

Primitive Streams such as DoubleStream provide termination operations such as sum () and average () as well as count () / min () / max ().

It is convenient to get the aggregated value without a loop, but if you try to calculate using the aggregated value, you will have to run Stream many times, which is inefficient. For that reason, there is also a termination operation called summaryStatistics () that can take each aggregated value in one shot.

2.2. reduce()

Use reduce () if you want to apply a custom aggregate function. For example, it can be used for join processing that cannot be simply realized by join.

    //Extract the last element
    Optional<String> last = stream.reduce((l, r) -> r);

    //Convert domain name to package name
    String domain = "hoge.example.co.jp";
    //Reverse the element order
    String pkg = Stream.of(domain.split("\\."))
            .reduce((l, r) -> r + "." + l).get();
    // jp.co.example.hoge

To be honest, I couldn't understand how to use it. .. I will look it up later and add it to the article.

3. Convert

3.1. toArray()

toArray () converts Stream to an array of its elements.


Stream<String> stream = Stream.of("a", "b", ...);

//No arguments
Object[] arr = stream.toArray();

//See array constructor
String[] arr = stream.toArray(String[]::new);

//Lambda expression
String[] arr = stream.toArray((size) -> new String[size]);

3.2. collect() Convert from stream to List. As an argument, call and define a method that is collected as a collection with static factory methods in java.util.stream.Collectors.

Java Collector Memo (Hishidama's Java8 Collector Memo)

//You can omit the class name by statically importing Collectors.
//Wildcards depending on IDE settings(*)May not let you use.
import static java.util.stream.Collectors.*;

    Stream<String> stream = ... ;

    //Convert to string(Linking)
    String text = stream.collect(joining());

    //Convert to string(Delimiter specification)
    String csv = src.stream().collect(joining(", "));

    //Convert to List
    List<String> list = stream.collect(toList()); // ArrayList

    //Convert to any List class
    List<String> list = stream
            .collect(toCollection(LinkedList::new)); // LinkedList

    //Convert to Set
    Set<String> set = stream.collect(toSet());  // HashSet

    //Convert to any Set class
    SortedSet<String> set = stream
            .collect(toCollection(TreeSet::new)); //TreeSet sorted

    LinkedHashSet<String> set = stream
            .collect(toCollection(LinkedHashSet::new)); //Maintain LinkedHashSet element order

    //Convert to Map
    // id -> object
    Map<Integer, User> map = users.stream()
            .collect(toMap(
                    e -> e.getId(),  //Exception if key is duplicated
                    e -> e           // value
            )); // HashMap

    // id -> name
    Map<Integer, User> map = users.stream()
            .collect(toMap(User::getId, User::getName));

    //Convert to any Map class
    SortedMap<Integer, User> map = users.stream()
            .collect(toMap(
                    e -> e.getId(),
                    e -> e,
                    (l, r) -> r, //Overwrite if the key is duplicated
                    TreeMap::new
            )); // TreeMap

4. Output

4.1. forEach() / forEachOrdered()

forEach () is a termination operation that can output and convert the sent stream data.


    List<String> list = Arrays.asList("a", "b", "c");

    //Stream forEach()Is a side effect
    //I want to clarify using blocks
    list.stream()
            .forEach(s -> {
                System.out.println(s);
            });

    //If you don't use blocks, you can see it and return the value.
    list.stream().forEach(s -> System.out.println(s));

    // forEach()Is awkward to use method references
    list.stream().forEach(System.out::println);

    //Iterable forEach()
    list.forEach(s -> {
        System.out.println(s);
    });

    //Equivalent extension for syntax
    for (String s : list) {
        System.out.println(s);
    }

    Map<String, String> map = new HashMap<>();

    //Actually it is also on Map
    map.forEach((key, val) -> {
        System.out.format("%s=%s\n", key, val);
    });

    //I want to use Stream with Map
    map.entrySet().stream()
            .forEach(e -> {
                System.out.format("%s=%s\n", e.getKey(), e.getValue());
            });

reference

Stream API basics I tried to stream the text to Java 8 Stream API (Termination operation)

Recommended Posts

I tried to summarize the Stream API
[java8] To understand the Stream API
I tried to explain the method
I tried using Java8 Stream API
I tried to summarize Java learning (1)
I tried to summarize Java 8 now
I tried to summarize the basic grammar of Ruby briefly
I tried to summarize Java lambda expressions
I tried to implement the Iterator pattern
What is Docker? I tried to summarize
I tried to summarize the words that I often see in docker-compose.yml
I tried to summarize what was asked at the site-java edition-
[Ruby] Tonight, I tried to summarize the loop processing [times, break ...]
Special Lecture on Multi-Scale Simulation: I tried to summarize the 5th
Special Lecture on Multi-Scale Simulation: I tried to summarize the 8th
I tried to summarize the methods of Java String and StringBuilder
Special Lecture on Multi-Scale Simulation: I tried to summarize the 7th
[Rails] I tried to raise the Rails version from 5.0 to 5.2
I tried to organize the session in Rails
[Must see !!!] I tried to summarize object orientation!
I tried to set tomcat to run the Servlet.
I tried to summarize the stumbling points when developing an Android application
I tried to summarize the key points of gRPC design and development
[Introduction to Java] I tried to summarize the knowledge that I think is essential
[Ruby] I tried to summarize the methods that frequently appear in paiza
[Ruby] I tried to summarize the methods that frequently appear in paiza ②
I tried to organize the cases used in programming
I tried to decorate the simple calendar a little
05. I tried to stub the source of Spring Boot
I tried to reduce the capacity of Spring Boot
I tried to summarize again the devise that was difficult at first sight
I tried the Docker tutorial!
I tried to draw animation with Blazor + canvas API
I tried the VueJS tutorial!
[JavaScript] The strongest case when I tried to summarize the parts I do not understand
I tried the FizzBuzz problem
I tried node-jt400 (SQL stream)
[Java] Introduction to Stream API
I tried to verify yum-cron
I tried to implement the Euclidean algorithm in Java
I tried to check the operation of http request (Put) with Talented API Tester
I tried to summarize the points to consider when acquiring location information with the iOS application ①
I tried to summarize the points to consider when acquiring location information with the iOS application ②
I tried to implement the like function by asynchronous communication
I tried to introduce Bootstrap 4 to the Rails 6 app [for beginners]
[JDBC] I tried to access the SQLite3 database from Java.
[Swift] I tried to implement the function of the vending machine
I tried to introduce UI animation to Pokedex using Poké API
I want to use the Java 8 DateTime API slowly (now)
I tried to link chat with Minecraft server with Discord API
I tried to build the environment little by little using docker
I tried to summarize personally useful apps and development tools (development tools)
What I was addicted to with the Redmine REST API
I tried to build the environment of WSL2 + Docker + VSCode
I tried to summarize personally useful apps and development tools (Apps)
Now is the time to get started with the Stream API
I was addicted to using Java's Stream API in Scala
I tried validation to unify the way hashtags are written
I tried to summarize object orientation in my own way.
I tried upgrading from CentOS 6.5 to CentOS 7 with the upgrade tool
I tried to chew C # (indexer)