Java 8 ~ Stream API ~ to start now

Last time: Java8 ~ forEach and lambda expression ~

Next is the Stream API edition.

What is Stream API?

It is an API introduced from Java8 that can describe processing (aggregation operation) for arrays and collections. Follow the procedure below for a set of elements.

  1. Generation: Generate Stream from arrays, collections, files, etc.
  2. Intermediate operation: Extract, sort, process values, etc. by specifying conditions for Stream.
  3. Termination operation: Convert from Stream to Collection, aggregate, get maximum / minimum value, etc. The termination operation can be performed only once for one Stream.

It's completely different from how to write up to Java7, so I'll summarize it using an example.

example

--The order file has the item code and order quantity --The product master file has the product code and product name. --Create a program that reads a certain order file and outputs the product code, product name extracted from the product master, and order quantity to the file. --If there is no product code record in the product master, leave the product name in the output file blank. --Sort in ascending order of product code

Input file specifications

Common

--CSV format --Do not include header lines

Order file

--File items are `` product code, order quantity''

order.csv


BBJ001,300
AES010,20
BBJ005,100
BBJ001,50
DIH999,10
AES010,150

Product name master

--File items should be `` product code, product name''

item.csv


BBJ001,Ballpoint pen black
BBJ005,Ballpoint pen red
AES010,eraser

Output file specifications

--CSV format --Do not include header lines --File items should be `` product code, product name, quantity''

order_collected.csv


DIH999,,10
BBJ005,Ballpoint pen red,100
BBJ001,Ballpoint pen black,300
BBJ001,Ballpoint pen black,50
AES010,eraser,20
AES010,eraser,150

procedure

If you drop this example into your code, I think the procedure would be like this.

  1. Read the product name master
  2. Break down one line into product code and product name
  3. Key: Product code, value: Map of product name
  4. Read the order file
  5. Sort the order file by product code
  6. Fill in the product name of the order file from the map of the product name master.
  7. Output to a file

Implementation with Stream API

** Procedure 1. Load the product name master **

Stream <String> java.nio.file.Files # lines (Path) to generate a Stream with all the lines in the file. Before Java7, Reader etc. were generated and read one by one.

Java7 version file reading


BufferedReader reader = new BufferedReader(new FileReader("files/stream/item.csv"));
String line = null;
while ((line = reader.readLine()) != null) {
    //Various processing for one line
}

Now this.

Stream API version file reading


Stream<String> itemStream = Files.lines(Paths.get("files/stream/item.csv"));

** Step 2. Break down one line into product code and product name **

Decompose each element of the Stream created in the previous step.

Stream<String[]> itemStream2 = itemStream .map(line -> line.split(","));

<R> Stream<R> map(Function<? super T,? extends R> mapper)

The intermediate operation map returns a Stream that is the result of applying an argument lambda expression to each element of the Stream. This time we are passing the lambda expression line-> line.split (",").

Process line.split (",") for each element line of Stream <String> itemStream and stream of type Stream <String []> Has been generated.

BBJ001,Ballpoint pen black// ⇒ [BBJ001,Ballpoint pen black]
BBJ005,Ballpoint pen red// ⇒ [BBJ005,Ballpoint pen red]
・ ・ ・

The important thing is that ** Stream intermediate operations return Stream **. Intermediate operations and termination operations can be performed on the results of intermediate operations.

** Step 3. Key: Product code, value: Map of product name (Product map) **

If it is Stream as it is, it will be difficult to handle with the product name assignment in the later procedure, so convert it to Map.

Map<String, String> items = itemStream2.collect(Collectors.toMap(item -> item[0], item -> item[1]));

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

The termination operation collect returns the result of applying a certain rule (Collector) to each element of Stream. Frequently used rules are available in Collectors.

Here, Collector toMap (<Lambda expression that creates a Map Key>, <Lambda expression that creates a Map Value>) that returns a Map is used.

Since itemStream2 is a Stream of String [], we generated a Map with the 0th element of the array as the Key and the 1st element as the Value.

Product name master summary

** Intermediate operations of Stream return Stream **, so you can write a chain from the creation of Stream to the conversion to Map.

//Read product name file
Map<String, String> items = Files.lines(Paths.get("files/stream/item.csv") // Stream<String>
	//Convert one line of file to an array(Preparation for Map)
	.map(line -> line.split(","))                                      // Stream<String[]>
	//Convert to Map
	.collect(Collectors.toMap(item -> item[0], item -> item[1]));      // Map<String, String>

** Step 4. Read (generate) the order file **

Next, we will start processing the order file. It is the same as the product name master up to the point where one line of data is decomposed.

Stream<String> orderStream = Files.lines(Paths.get("files/stream/order.csv"))
Stream<OrderDto> orderStream2 = orderStream
				//Convert one line of file to an array(Preparation for Dto)
				.map(line -> line.split(","))
				//Convert array to delivery Dto
				.map(array -> makeOrder(array));

After splitting with ",", I mapped it to OrderDto with makeOrder (String []).

private static OrderDto makeOrder(String[] array) {
	OrderDto order = new OrderDto();
	int idx = 0;
	order.setItemCode(array[idx++]);
	order.setOrderNum(Integer.parseInt(array[idx++]));
	return order;
}

** Step 5. Sort the order file by product code (intermediate operation) **

Sort in ascending order of product code.

Stream<OrderDto> orderStream3 = orderStream2.sorted((item1, item2) -> item1.getItemCode().compareTo(item2.getItemCode()));

Stream<T> sorted(Comparator<? super T> comparator)

Comparator defines comparison rules between objects Functional interface. If you implement the interface Comparable, it has no argumentsYou can also usesorted () .

You can set a default sort order in the interface Comparable and use Comparator to redefine it concisely with a lambda expression when needed.

** Step 6. Fill in the product name of the order file from the product name master Map (intermediate operation) **

Search the product map by product code, and if it hits, fill in the product name, and if it does not hit, fill in the empty string.

Stream<OrderDto> orderStream4 = orderStream2.map(order -> {
					order.setItemName(Optional.ofNullable(items.get(order.getItemCode())).orElse(""));
					return order;
				});

Use map again. Optional is not explained in detail here, but it is used as a function to determine the default value when it is Null.

** Step 7. Output to file (termination operation) **

Now that the data is complete, output it to a file.

try (BufferedWriter writer = new BufferedWriter(new FileWriter("files/stream/order_collected.csv")) ) {
	orderList.forEach(order -> {
		try {
			writer.write(makeLine(order));
			writer.newLine();
		} catch (IOException e) {e.printStackTrace();return;}
	});
}

void forEach(Consumer<? super T> action)

The termination operation forEach used in the previous explanation of the lambda expression executes the operation specified by the argument for each element.

Complete

public class StreamMain {

	public static void main(String[] args) {
		try (Stream<String> orderStream = Files.lines(Paths.get("files/stream/order.csv"));
				Stream<String> itemStream = Files.lines(Paths.get("files/stream/item.csv"))){

			//Read order file
			Map<String, String> items = itemStream
					//Convert one line of file to an array(Preparation for Map)
					.map(line -> line.split(","))
					//Convert to Map
					.collect(Collectors.toMap(item -> item[0], item -> item[1]));

			//Read product name file
			Stream<OrderDto> orderList = orderStream
				//Convert one line of file to an array(Preparation for Dto)
				.map(line -> line.split(","))
				//Convert array to delivery Dto
				.map(array -> makeOrder(array))
				//sort
				.sorted((item1, item2) -> item1.getItemCode().compareTo(item2.getItemCode()))
				//matching
				.map(order -> {
					order.setItemName(Optional.ofNullable(items.get(order.getItemCode())).orElse(""));
					return order;
				});

			//output
			try (BufferedWriter writer = new BufferedWriter(new FileWriter("files/stream/order_collected.csv")) ) {
				orderList.forEach(order -> {
					try {
						writer.write(makeLine(order));
						writer.newLine();
					} catch (IOException e) {e.printStackTrace();return;}
				});
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private static String makeLine(OrderDto order) {
		StringBuilder line = new StringBuilder();
		line.append(order.getItemCode());
		line.append(',');
		line.append(order.getItemName());
		line.append(',');
		line.append(order.getOrderNum());
		return line.toString();
	}

	private static OrderDto makeOrder(String[] array) {
		OrderDto order = new OrderDto();
		int idx = 0;
		order.setItemCode(array[idx++]);
		order.setOrderNum(Integer.parseInt(array[idx++]));
		return order;
	}
}

What is Re: Stream API?

In solving the example, I first organized the procedure.

  1. Read the product name master
  2. Break down one line into product code and product name
  3. key: product code, value: map of product name

Probably, in Java7 or earlier code, even Map is generated at the time of for loop to read the product name master.

Java7


for (/*Set of elements*/) {
    //Various processing for one element
}

By the way, the Stream API operates on a set of elements. Typical operations are map (convert an element to another element), filter (extract the one that meets the conditions from the elements / unused this time), `` sortedFor example,(sort elements). Furthermore, since the return value of the intermediate operation is Stream, it is suitable to first create a set of elements and then operate continuously.

Java8


Stream obj = //Convert a set of elements to Stream
  //Converting elements in obj
  .map(/*Conversion rules*/)
  //Filtering
  .filter(/*Extraction rules*/);

When using the Stream API, it seems that you should think about the procedure so that it is a combination of simple operations on the elements.

The following article was very helpful. Write Java8-like code in Java8

bonus

I wanted to find the total order quantity for each product code, but I gave up because I couldn't find out how to write it well. I will leave the memorial code of the version I forced, so please let me know if you have any good hands.

//Read product name file
Stream<OrderDto> orderList = orderStream
	//Convert one line of file to an array(Preparation for Dto)
	.map(line -> line.split(","))
	//Convert array to delivery Dto
	.map(array -> makeOrder(array));

//Group by product code for aggregation
Map<String, List<OrderDto>> grouping = orderList.collect(Collectors.groupingBy(order->order.getItemCode()));
//Sum the order quantity for each grouped element
orderList = grouping.values()
		.stream()
		.map(orders->{
			//Pick up any one of the elements
			OrderDto order = orders.stream().findAny().get();
			//Store the total number of all elements in the order quantity
			order.setOrderNum(orders.stream().mapToInt(oo ->oo.getOrderNum()).sum());
			return order;
		})
		//sort
		.sorted((item1, item2) -> item1.getItemCode().compareTo(item2.getItemCode()))
		//Set product name
		.map(order -> {
			order.setItemName(Optional.ofNullable(items.get(order.getItemCode())).orElse(""));
			return order;
		});

Recommended Posts

Java 8 ~ Stream API ~ to start now
Java 8 to start now ~ Date time API ~
[Java] Introduction to Stream API
Java8 to start now ~ Optional ~
[java8] To understand the Stream API
[Introduction to Java] About Stream API
Java Stream API
Try various Java Stream API methods (now)
[Java] Stream API / map
Java8 Stream API practice
Java8 to start now ~ forEach and lambda expression ~
[Java] How to operate List using Stream API
Java Stream API cheat sheet
Java Stream API in 5 minutes
[Java] Stream API --Stream termination processing
[Java] Stream API --Stream intermediate processing
[Java] Stream API intermediate operation
[Must-see for apprentice java engineer] How to use Stream API
I tried using Java8 Stream API
Java, interface to start from beginner
Now is the time to get started with the Stream API
Java, arrays to start with beginners
I tried to summarize Java 8 now
Convert 2D array to csv format with Java 8 Stream API
Articles to learn more about Stream API
Data processing using stream API from Java 8
Try using the Stream API in Java
Nowadays Java lambda expressions and Stream API
Anonymous class (aiming to introduce stream api)
I tried to summarize the Stream API
Stream API memo
Try Java 8 Stream
[Java] Introduction to Java
Studying Java 8 (Stream)
Introduction to java
Java Stream termination
[Java] Stream processing
Stream API basics
Java 9 Optional :: stream
Java8 / 9 Beginners: Stream API addiction points and how to deal with them
Two ways to start a thread in Java + @
How to use Java API with lambda expression
Features that are likely to enter Java 10 for now
List processing to understand with pictures --java8 stream / javaslang-
Sample code to convert List to List <String> in Java Stream
Changes from Java 8 to Java 11
Sum from Java_1 to 100
[Java] Stream Collectors notes
[In-house study session] Java basics-Lambda expression and Stream API- (2017/07/13)
Stream API (Collectors class)
[Java] Connect to MySQL
Use Java lambda expressions outside of the Stream API
Stream API map method
Kotlin's improvements to Java
Java development for beginners to start from 1-Vol.1-eclipse setup
From Java to Ruby !!
Zabbix API in Java
Java8 Stream reduction operation
Summary of Java communication API (1) How to use Socket
Review Java annotations now
Summary of Java communication API (3) How to use SocketChannel