Posts

Java 8 – Should we close the Stream after use?

Only Streams whose source are an IO channel like Files.lines(Path, Charset) need to be closed.

Read this Stream JavaDocs

Streams have a BaseStream.close() method and implement AutoCloseable, but nearly all stream instances do not actually need to be closed after use. Generally, only streams whose source is an IO channel (such as those returned by Files.lines(Path, Charset)) will require closing. Most streams are backed by collections, arrays, or generating functions, which require no special resource management. (If a stream does require closing, it can be declared as a resource in a try-with-resources statement.)

1. For normal Stream like this, the stream instance does not need to be closed after use.Java8StreamExample.java

package com.mkyong;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Java8StreamExample {

    public static void main(String[] args) {

        Stream<String> stream = Stream.of("A", "B", "C");

        List<String> filter = stream.filter(x -> !x.equalsIgnoreCase("B"))
 .collect(Collectors.toList());

        // no need close the stream.
        //stream.close();

        System.out.println(filter); // [A, C]

    }

}

2. For Stream whose source are an IO channel, close it with try-with-resourcesJava8StreamIO.java

package com.mkyong;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Java8StreamIO {

    public static void main(String[] args) {

        String path = "c:\\projects\\app.log";

		// auto close
        try (Stream<String> lines = Files.lines(Paths.get(path))) {

            String content = lines.collect(Collectors.joining(System.lineSeparator()));
            
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

Java 8 Parallel Streams Examples

Few Java 8 examples to execute streams in parallel.

1. BaseStream.parallel()

A simple parallel example to print 1 to 10.ParallelExample1.java

package com.mkyong.java8;

import java.util.stream.IntStream;

public class ParallelExample1 {

    public static void main(String[] args) {

        System.out.println("Normal...");
      
 IntStream range = IntStream.rangeClosed(1, 10);
        range.forEach(System.out::println);

        System.out.println("Parallel...");

        IntStream range2 = IntStream.rangeClosed(1, 10);
        range2.parallel().forEach(System.out::println);

    }

}

Output

Normal...
1
2
3
4
5
6
7
8
9
10

Parallel...
7
6
8
9
10
1
4
5
3
2

2. Collection.parallelStream()

Another simple parallel example to print a to z. For collection, we can use parallelStream().ParallelExample2.java

package com.mkyong.java8;

import java.util.ArrayList;
import java.util.List;

public class ParallelExample2 {

    public static void main(String[] args) {

        System.out.println("Normal...");

        List<String> alpha = getData();
        alpha.stream().forEach(System.out::println);

        System.out.println("Parallel...");

        List<String> alpha2 = getData();
        alpha2.parallelStream().forEach(System.out::println);
        
    }

    private static List<String> getData() {

        List<String> alpha = new ArrayList<>();

        int n = 97;  // 97 = a , 122 = z
        while (n <= 122) {
            char c = (char) n;
            alpha.add(String.valueOf(c));
            n++;
        }

        return alpha;

    }

}

Output

Normal...
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
Parallel...
q
s
r
o
x
h
l
p
d
i
g
t
u
n
z
v
j
k
w
f
m
c
a
e
b
y

3. Is Stream running in parallel mode?

3.1 We can test it with isParallel()ParallelExample3a.java

package com.mkyong.java8;

import java.util.stream.IntStream;

public class ParallelExample3a {

    public static void main(String[] args) {

        System.out.println("Normal...");

        IntStream range = IntStream.rangeClosed(1, 10);
        System.out.println(range.isParallel());         // false
        range.forEach(System.out::println);

        System.out.println("Parallel...");

        IntStream range2 = IntStream.rangeClosed(1, 10);
        IntStream range2Parallel = range2.parallel();
        System.out.println(range2Parallel.isParallel()); // true
        range2Parallel.forEach(System.out::println);

    }

}

3.2 Or print the current thread name like this:ParallelExample3b.java

package com.mkyong.java8;

import java.util.stream.IntStream;

public class ParallelExample3b {

    public static void main(String[] args) {

        System.out.println("Normal...");

        IntStream range = IntStream.rangeClosed(1, 10);
        range.forEach(x -> {
            System.out.println("Thread : " + Thread.currentThread().getName() + ", value: " + x);
        });

        System.out.println("Parallel...");

        IntStream range2 = IntStream.rangeClosed(1, 10);
        range2.parallel().forEach(x -> {
            System.out.println("Thread : " + Thread.currentThread().getName() + ", value: " + x);
        });

    }

}

Output

Normal...
Thread : main, value: 1
Thread : main, value: 2
Thread : main, value: 3
Thread : main, value: 4
Thread : main, value: 5
Thread : main, value: 6
Thread : main, value: 7
Thread : main, value: 8
Thread : main, value: 9
Thread : main, value: 10

Parallel...
Thread : main, value: 7
Thread : main, value: 6
Thread : ForkJoinPool.commonPool-worker-5, value: 3
Thread : ForkJoinPool.commonPool-worker-7, value: 8
Thread : ForkJoinPool.commonPool-worker-5, value: 5
Thread : ForkJoinPool.commonPool-worker-5, value: 4
Thread : ForkJoinPool.commonPool-worker-3, value: 9
Thread : ForkJoinPool.commonPool-worker-5, value: 1
Thread : ForkJoinPool.commonPool-worker-7, value: 2
Thread : ForkJoinPool.commonPool-worker-9, value: 10

P.S By default, parallel streams use `ForkJoinPool`

4. Calculation

4.1 Java 8 streams to print all prime numbers up to 1 million:ParallelExample4.java

package com.mkyong.java8;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class ParallelExample4 {

    public static void main(String[] args) {

        long count = Stream.iterate(0, n -> n + 1)
                .limit(1_000_000)
                //.parallel()   with this 23s, without this 1m 10s
                .filter(ParallelExample4::isPrime)
                .peek(x -> System.out.format("%s\t", x))
                .count();

        System.out.println("\nTotal: " + count);

    }

    public static boolean isPrime(int number) {
        if (number <= 1) return false;
        return !IntStream.rangeClosed(2, number / 2).anyMatch(i -> number % i == 0);
    }

}

Result:

  • For normal streams, it takes 1 minute 10 seconds.
  • For parallel streams, it takes 23 seconds.

P.S Tested with i7-7700, 16G RAM, WIndows 10

4.2 Yet another parallel stream example to find out the average age of a list of employees.

 List<Employee> employees = obj.generateEmployee(10000);

 double age = employees
 .parallelStream()
 .mapToInt(Employee::getAge)
 .average()
 .getAsDouble();

    System.out.println("Average age: " + age); 

5. Case Study

5.1 Parallel streams to increase the performance of a time-consuming save file tasks.

This Java code will generate 10,000 random employees and save into 10,000 files, each employee save into a file.

  • For normal stream, it takes 27-29 seconds.
  • For parallel stream, it takes 7-8 seconds.

P.S Tested with i7-7700, 16G RAM, WIndows 10ParallelExample5.java

package com.mkyong.java8;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ParallelExample5 {

    private static final String DIR = System.getProperty("user.dir") + "/test/";

    public static void main(String[] args) throws IOException {

        Files.createDirectories(Paths.get(DIR));

        ParallelExample5 obj = new ParallelExample5();

        List<Employee> employees = obj.generateEmployee(10000);

 // normal, sequential
        //employees.stream().forEach(ParallelExample5::save);  // 27s-29s

 // parallel
        employees.parallelStream().forEach(ParallelExample5::save); // 7s-8s

    }

    private static void save(Employee input) {

        try (FileOutputStream fos = new FileOutputStream(new File(DIR + input.getName() + ".txt"));
             ObjectOutputStream obs = new ObjectOutputStream(fos)) {
            obs.writeObject(input);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private List<Employee> generateEmployee(int num) {

        return Stream.iterate(0, n -> n + 1)
                .limit(num)
                .map(x -> {
                    return new Employee(
                            generateRandomName(4),
                            generateRandomAge(15, 100),
                            generateRandomSalary(900.00, 200_000.00)
                    );
                })
                .collect(Collectors.toList());

    }

    private String generateRandomName(int length) {

        return new Random()
                .ints(5, 97, 122) // 97 = a , 122 = z
                .mapToObj(x -> String.valueOf((char) x))
                .collect(Collectors.joining());

    }

    private int generateRandomAge(int min, int max) {
        return new Random()
                .ints(1, min, max)
                .findFirst()
                .getAsInt();
    }

    private BigDecimal generateRandomSalary(double min, double max) {
        return new BigDecimal(new Random()
                .doubles(1, min, max)
                .findFirst()
                .getAsDouble()).setScale(2, RoundingMode.HALF_UP);
    }

}

Employee.java

package com.mkyong.java8;

import java.io.Serializable;
import java.math.BigDecimal;

public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private BigDecimal salary;

    //getters, setters n etc...

}

Java 8 forEach examples

In this article, we will show you how to loop a List and a Map with the new Java 8 forEach statement.

1. forEach and Map

1.1 Normal way to loop a Map.

 Map<String, Integer> items = new HashMap<>();
 items.put("A", 10);
 items.put("B", 20);
 items.put("C", 30);
 items.put("D", 40);
 items.put("E", 50);
 items.put("F", 60);

 for (Map.Entry<String, Integer> entry : items.entrySet()) {
 System.out.println("Item : " + entry.getKey() + " Count : " + entry.getValue());
 }

1.2 In Java 8, you can loop a Map with forEach + lambda expression.

 Map<String, Integer> items = new HashMap<>();
 items.put("A", 10);
 items.put("B", 20);
 items.put("C", 30);
 items.put("D", 40);
 items.put("E", 50);
 items.put("F", 60);
 
 items.forEach((k,v)->System.out.println("Item : " + k + " Count : " + v));
 
 items.forEach((k,v)->{
 System.out.println("Item : " + k + " Count : " + v);
 if("E".equals(k)){
 System.out.println("Hello E");
 }
 });

2. forEach and List

2.1 Normal for-loop to loop a List.

 List<String> items = new ArrayList<>();
 items.add("A");
 items.add("B");
 items.add("C");
 items.add("D");
 items.add("E");

 for(String item : items){
 System.out.println(item);
 }

2.2 In Java 8, you can loop a List with forEach + lambda expression or method reference.

	List<String> items = new ArrayList<>();
	items.add("A");
	items.add("B");
	items.add("C");
	items.add("D");
	items.add("E");

	//lambda
	//Output : A,B,C,D,E
	items.forEach(item->System.out.println(item));
		
	//Output : C
	items.forEach(item->{
		if("C".equals(item)){
			System.out.println(item);
		}
	});
		
	//method reference
	//Output : A,B,C,D,E
	items.forEach(System.out::println);
	
	//Stream and filter
	//Output : B
	items.stream()
		.filter(s->s.contains("B"))
		.forEach(System.out::println);

Java 8 forEach examples

In this article, we will show you how to loop a List and a Map with the new Java 8 forEach statement.

1. forEach and Map

1.1 Normal way to loop a Map.

 Map<String, Integer> items = new HashMap<>();
 items.put("A", 10);
 items.put("B", 20);
 items.put("C", 30);
 items.put("D", 40);
 items.put("E", 50);
 items.put("F", 60);

 for (Map.Entry<String, Integer> entry : items.entrySet()) {
 System.out.println("Item : " + entry.getKey() + " Count : " + entry.getValue());
 }

1.2 In Java 8, you can loop a Map with forEach + lambda expression.

 Map<String, Integer> items = new HashMap<>();
 items.put("A", 10);
 items.put("B", 20);
 items.put("C", 30);
 items.put("D", 40);
 items.put("E", 50);
 items.put("F", 60);
 
 items.forEach((k,v)->System.out.println("Item : " + k + " Count : " + v));
 
 items.forEach((k,v)->{
 System.out.println("Item : " + k + " Count : " + v);
 if("E".equals(k)){
 System.out.println("Hello E");
 }
 });

2. forEach and List

2.1 Normal for-loop to loop a List.

 List<String> items = new ArrayList<>();
 items.add("A");
 items.add("B");
 items.add("C");
 items.add("D");
 items.add("E");

 for(String item : items){
 System.out.println(item);
 }

2.2 In Java 8, you can loop a List with forEach + lambda expression or method reference.

	List<String> items = new ArrayList<>();
	items.add("A");
	items.add("B");
	items.add("C");
	items.add("D");
	items.add("E");

	//lambda
	//Output : A,B,C,D,E
	items.forEach(item->System.out.println(item));
		
	//Output : C
	items.forEach(item->{
		if("C".equals(item)){
			System.out.println(item);
		}
	});
		
	//method reference
	//Output : A,B,C,D,E
	items.forEach(System.out::println);
	
	//Stream and filter
	//Output : B
	items.stream()
		.filter(s->s.contains("B"))
		.forEach(System.out::println);

Java 8 – Convert List to Map

Few Java 8 examples to show you how to convert a List of objects into a Map, and how to handle the duplicated keys.Hosting.java

package com.mkyong.java8

public class Hosting {

    private int Id;
    private String name;
    private long websites;

    public Hosting(int id, String name, long websites) {
        Id = id;
        this.name = name;
        this.websites = websites;
    }

    //getters, setters and toString()
}

1. List to Map – Collectors.toMap()

Create a list of the Hosting objects, and uses Collectors.toMap to convert it into a Map.TestListMap.java

package com.mkyong.java8

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TestListMap {

    public static void main(String[] args) {

        List<Hosting> list = new ArrayList<>();
        list.add(new Hosting(1, "liquidweb.com", 80000));
        list.add(new Hosting(2, "linode.com", 90000));
        list.add(new Hosting(3, "digitalocean.com", 120000));
        list.add(new Hosting(4, "aws.amazon.com", 200000));
        list.add(new Hosting(5, "mkyong.com", 1));

        // key = id, value - websites
        Map<Integer, String> result1 = list.stream().collect(
                Collectors.toMap(Hosting::getId, Hosting::getName));

        System.out.println("Result 1 : " + result1);

        // key = name, value - websites
        Map<String, Long> result2 = list.stream().collect(
                Collectors.toMap(Hosting::getName, Hosting::getWebsites));

        System.out.println("Result 2 : " + result2);

        // Same with result1, just different syntax
        // key = id, value = name
        Map<Integer, String> result3 = list.stream().collect(
                Collectors.toMap(x -> x.getId(), x -> x.getName()));

        System.out.println("Result 3 : " + result3);
    }
}

Output

Result 1 : {1=liquidweb.com, 2=linode.com, 3=digitalocean.com, 4=aws.amazon.com, 5=mkyong.com}
Result 2 : {liquidweb.com=80000, mkyong.com=1, digitalocean.com=120000, aws.amazon.com=200000, linode.com=90000}
Result 3 : {1=liquidweb.com, 2=linode.com, 3=digitalocean.com, 4=aws.amazon.com, 5=mkyong.com}

2. List to Map – Duplicated Key!

2.1 Run below code, and duplicated key errors will be thrown!TestDuplicatedKey.java

package com.mkyong.java8;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TestDuplicatedKey {

    public static void main(String[] args) {

        List<Hosting> list = new ArrayList<>();
        list.add(new Hosting(1, "liquidweb.com", 80000));
        list.add(new Hosting(2, "linode.com", 90000));
        list.add(new Hosting(3, "digitalocean.com", 120000));
        list.add(new Hosting(4, "aws.amazon.com", 200000));
        list.add(new Hosting(5, "mkyong.com", 1));
 
        list.add(new Hosting(6, "linode.com", 100000)); // new line

        // key = name, value - websites , but the key 'linode' is duplicated!?
        Map<String, Long> result1 = list.stream().collect(
                Collectors.toMap(Hosting::getName, Hosting::getWebsites));

        System.out.println("Result 1 : " + result1);

    }
}

Output – The error message below is a bit misleading, it should show “linode” instead of the value of the key.

Exception in thread "main" java.lang.IllegalStateException: Duplicate key 90000
 at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
 at java.util.HashMap.merge(HashMap.java:1245)
 //...

2.2 To solve the duplicated key issue above, pass in the third mergeFunction argument like this :

 Map<String, Long> result1 = list.stream().collect(
                Collectors.toMap(Hosting::getName, Hosting::getWebsites,
                        (oldValue, newValue) -> oldValue
                )
        );

Output

Result 1 : {..., aws.amazon.com=200000, linode.com=90000}

Note
(oldValue, newValue) -> oldValue ==> If the key is duplicated, do you prefer oldKey or newKey?

3.3 Try newValue

 Map<String, Long> result1 = list.stream().collect(
                Collectors.toMap(Hosting::getName, Hosting::getWebsites,
                        (oldValue, newValue) -> newvalue
                )
        );

Output

Result 1 : {..., aws.amazon.com=200000, linode.com=100000}

3. List to Map – Sort & Collect

TestSortCollect.java

package com.mkyong.java8;

import java.util.*;
import java.util.stream.Collectors;

public class TestSortCollect {

    public static void main(String[] args) {

        List<Hosting> list = new ArrayList<>();
        list.add(new Hosting(1, "liquidweb.com", 80000));
        list.add(new Hosting(2, "linode.com", 90000));
        list.add(new Hosting(3, "digitalocean.com", 120000));
        list.add(new Hosting(4, "aws.amazon.com", 200000));
        list.add(new Hosting(5, "mkyong.com", 1));
        list.add(new Hosting(6, "linode.com", 100000));

        //example 1
        Map result1 = list.stream()
                .sorted(Comparator.comparingLong(Hosting::getWebsites).reversed())
                .collect(
                        Collectors.toMap(
                                Hosting::getName, Hosting::getWebsites, // key = name, value = websites
                                (oldValue, newValue) -> oldValue,       // if same key, take the old key
                                LinkedHashMap::new                      // returns a LinkedHashMap, keep order
                        ));

        System.out.println("Result 1 : " + result1);

    }
}

Output

Result 1 : {aws.amazon.com=200000, digitalocean.com=120000, linode.com=100000, liquidweb.com=80000, mkyong.com=1}
Copy

P.S In above example, the stream is sorted before collect, so the “linode.com=100000” became the ‘oldValue’.

Java 8 – How to sort list with stream.sorted()

Few examples to show you how to sort a List with stream.sorted()

1. List

1.1 Sort a List with Comparator.naturalOrder()

package com.mkyong.sorted;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamApplication {

    public static void main(String[] args) {

        List<String> list = Arrays.asList("9", "A", "Z", "1", "B", "Y", "4", "a", "c");

        /* 
		List<String> sortedList = list.stream()
			.sorted(Comparator.naturalOrder())
			.collect(Collectors.toList());
			
        List<String> sortedList = list.stream()
			.sorted((o1,o2)-> o1.compareTo(o2))
			.collect(Collectors.toList());
		*/

		List<String> sortedList = list.stream().sorted().collect(Collectors.toList());
		
        sortedList.forEach(System.out::println);

    }
}
Copy

Output

1
4
9
A
B
Y
Z
a
c
Copy

1.2 Sort a List with Comparator.reverseOrder()

package com.mkyong.sorted;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class StreamApplication {

    public static void main(String[] args) {

        List<String> list = Arrays.asList("9", "A", "Z", "1", "B", "Y", "4", "a", "c");

        /*
		List<String> sortedList = list.stream()
			.sorted((o1,o2)-> o2.compareTo(o1))
			.collect(Collectors.toList());
		*/
		
        List<String> sortedList = list.stream()
			.sorted(Comparator.reverseOrder())
			.collect(Collectors.toList());

        sortedList.forEach(System.out::println);

    }
}
Copy

Output

c
a
Z
Y
B
A
9
4
1
Copy

2. List Objects

1.1 Sort by age, natural order.

package com.mkyong.sorted;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class StreamApplication {

    static List<User> users = Arrays.asList(
            new User("C", 30),
            new User("D", 40),
            new User("A", 10),
            new User("B", 20),
            new User("E", 50));

    public static void main(String[] args) {
        
        /*List<User> sortedList = users.stream()
			.sorted((o1, o2) -> o1.getAge() - o2.getAge())
			.collect(Collectors.toList());*/
			
        List<User> sortedList = users.stream()
			.sorted(Comparator.comparingInt(User::getAge))
			.collect(Collectors.toList());
 
        sortedList.forEach(System.out::println);

    }

    static class User {

        private String name;
        private int age;

        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
}
Copy

Output

User{name='A', age=10}
User{name='B', age=20}
User{name='C', age=30}
User{name='D', age=40}
User{name='E', age=50}
Copy

1.2 reverse order.

	List<User> sortedList = users.stream()
		.sorted(Comparator.comparingInt(User::getAge)
		.reversed())
		.collect(Collectors.toList());

    sortedList.forEach(System.out::println);
Copy

Output

User{name='E', age=50}
User{name='D', age=40}
User{name='C', age=30}
User{name='B', age=20}
User{name='A', age=10}
Copy

1.3 Sort by name

	/*List<User> sortedList = users.stream()
		.sorted((o1, o2) -> o1.getName().compareTo(o2.getName()))
		.collect(Collectors.toList());*/
    
	List<User> sortedList = users.stream()
		.sorted(Comparator.comparing(User::getName))
		.collect(Collectors.toList());
Copy