Stream explained with examples


Basic Usage

The Person object we will use in this article


    public class Person{
        int age;
        String name;
        boolean isFemail;
        public Person(String name, int age, boolean isFemale){
            this.name = name;
            this.age = age;
            this.isFemail = isFemale;
        }

        public Person(int age, boolean isFemale){
            this.age = age;
            this.isFemail = isFemale;
        }

        public boolean isFemale(){
            return this.isFemail;
        }

        public String getName(){
            return this.name;
        }
        public int getAge(){
            return this.age;
        }

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

        @Override
        public String toString(){
            return "name:" + name + " age: " + age + " isFemale: " + isFemale();
        }

        @Override
        public boolean equals(Object obj){
            if( obj == this)return true;
            if(!(obj instanceof Person))return false;

            Person p = (Person)obj;
            return p.getName().equals(this.name);
        }

        @Override
        public int hashCode(){
            return name.hashCode();
        }
    }

1. Remove Duplicate

List<Person> distinct = persons.stream().distinct().collect(Collectors.toList());

2. Sum with reduce()

int sum = persons.stream().map(e -> e.getAge()).reduce(0,(a,b) -> a + b);

use map() to get the age value and then use reduce() to add next numbers together.

3. Count

long count = persons.stream().count();

4. Average

double average = persons.stream().mapToInt(e -> e.getAge()).average().getAsDouble();

You also can use mapToLong, mapToDouble. mapToInt, mapToLong and mapToDouble are the “Boxing” version of map, it is same as map.

5. Pull the Attributes of object from list by map() and flatMap()

map() and flatMap() methods can pull data or Attribute on each element in the list. The Difference between 2 is:

map() 1 value in -> 1 value out flatMap() 1 value in -> 0..n values out

List<String> names = persons.stream().map(e -> e.getName()).collect(Collectors.toList());

Pull all the names of person from the list.

6. Update object from list by map() and flatMap() by multiple line lamba expression

List<Person> result = persons.stream().map(e -> {
    Person p = new Person(e.getName(), e.getAge(), e.isFemale());
    p.setAge(p.getAge() + 10);
    return p;
    }).collect(Collectors.toList());

This code will add 10 on each person’s age and then return new list

7. sort

List<Person> sorted = persons.stream().sorted(Comparator.comparingInt(e -> e.getAge()))
                      .collect(Collectors.toList());

sort by age small to big, you also can use customized sort compareator

Comparator<Person> reverseComparator = new Comparator<Person>() {
    @Override
    public int compare(Person e1, Person e2) {
        return e1.getAge() > e2.getAge() ? -1 : 1;
    }
};

List<Person> sorted = persons.stream().sorted(reverseComparator).collect(Collectors.toList());

reverse the sort, but you can use that method to apply on any sort

8. Find

  • Find One Element
 Optional<Person> femalePerson = persons.stream().filter(e -> e.isFemale()).findFirst();
 Optional<Person> femalePerson = persons.stream().filter(e -> e.isFemale()).findAny();
 if(femalePerson.isPresent()){
    System.out.println(femalePerson.get());
 }

findFirst() will return first found female person in the list if you are NOT use “parallel()”, findAny() return found female person but non-deterministic

  • Find Multiple Elements (by filter)

** I would like to call filter as one of find functions, it could return 1 or n elements from list.

List<Person> femalePersons = persons.stream()
    .filter(e -> e.isFemale()).collect(Collectors.toList());

for(Person female: femalePersons){
    System.out.println(female);
}
  • Find Multiple Elements by Predicate - separate the logic apart
Predicate<Person> ageGreaterThan10 = new Predicate<Person>() {
    @Override
    public boolean test(Person p) {
        return p.getAge() > 10;
    }
};

List<Person> result = persons.stream()
                             .filter(ageGreaterThan10)
                             .collect(Collectors.toList());

9. match

  • AnyMatch
boolean existFemale = persons.stream().anyMatch(e -> e.isFemale() );

This return true if any “person” in the list return isFemale true.

  • All Match
boolean allFemale = persons.stream().allMatch(e -> e.isFemale());

This will return true if and only if all person in the list is female.

  • None Match
boolean noFemale = persons.stream().noneMatch(e -> e.isFemale());

This will return true if and only if none of person in list is female.

10 Limit and Skip

List<Person> limit = persons.stream().limit(1).collect(Collectors.toList());

This will get the first element from the list, you can change the number in limit() to get more

List<Person> skip = persons.stream().skip(persons.size() - 1).collect(Collectors.toList());

This will get you the last element of the list

11 use filter() to parse String

Suppose we have String like this:

String s = "number1 1 number2 2 number3 3";

we want to parse out all the number: 1, 2 and 3, we can use following way:

List<Integer> l = Arrays.stream(s.split(" "))
                  .filter(e -> e.matches("\\d+"))
                  .mapToInt(i -> Integer.valueOf(i))
                  .boxed().collect(Collectors.toList());

you need to use boxed() because mapToInt() return is “int” type, you need to boxed to convert to Integer

12 use map to apply function on each element

You can use map method to apply a function (usually lambda) on each element and then return new value (MUST return)

    List<Integer> result = input.stream().parallel().map( e ->{
        System.out.println(e + " thread name: " + Thread.currentThread().getName());
        return e * 10;
    }).collect(Collectors.toList());

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

This shows for each integer in the list(input), multiply 10 and then add into another list(result)

13 Showcase 1: scan files in directory and proceed

Following code show provide the list of files in a directory as stream:

    private Stream<File> scanDirectory(Path directory){
        File[] files = directory.toFile().listFiles(this::fileFilter);
        if(files == null){
            throw new RuntimeException("can not list files for " + directory);
        }
        return Arrays.stream(files);
    }

    private boolean fileFilter(File file){
        return file.isFile() &&
                filePattern.matcher(file.getName()).find();
    }

then process method take stream and work on the file:

    public void process(){
        try {
            Path path = getWatchPath();
            scanDirectory(path).map(File::getAbsolutePath)
                    .filter(this::findNonTextFile)
                    .findFirst()
                    .ifPresent(filePath -> processFile(filePath, fileHandler.apply(filePath)));
        } catch (IOException e) {
//            e.printStackTrace();
            log.error("IOException {}", e);
        }
    }

scanDirectory(path) return a Stream object of File, then use map to get absolute path of each file then use ifPresent to proceed the file.