Package com.neiljbrown.examples.java8

Source Code of com.neiljbrown.examples.java8.StreamApiExamplesTest$ExamResult

/*
* Copyright 2014-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.neiljbrown.examples.java8;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.*;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.Month;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.OptionalInt;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.Test;

/**
* Examples of using the new Streams API in Java 8, and its companion <a
* href="http://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html">package of functional
* interfaces</a> to support processing 'streams'.
* <p>
* The <a href="http://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html">Streams API</a> is a new
* concurrency feature which builds on the existing fork-join framework to support parallel processing of data
* (utilising multi-core CPUs), whilst abstracting the complexity of the underlying multi-threading logic. A Stream can
* be defined as a sequence of data elements from a source which supports performing aggregate (‘bulk’) operations on
* the elements, both sequentially and in parallel. The source of a Stream can include Collections, Arrays, and some I/O
* classes (e.g. BufferedReader).
*/
public class StreamApiExamplesTest {

  /**
   * An example of how {@link java.util.stream.Stream#filter(java.util.function.Predicate)} can be used to declaratively
   * filter a {@link Collection} using a {@link Predicate}.
   */
  @Test
  public void testFilter() {
    // Create a list of students with different DOB
    final List<Student> students = new ArrayList<>();
    final Student s1 = new Student(LocalDate.of(1974, Month.JUNE, 21), "joe.bloggs@test.net");
    students.add(s1);
    final Student s2 = new Student(LocalDate.of(1980, Month.JANUARY, 2), "jane.bloggs@test.net");
    students.add(s2);
    final Student s3 = new Student(LocalDate.of(1976, Month.AUGUST, 7), "jim.bloggs@test.net");
    students.add(s3);
    final Student s4 = new Student(LocalDate.of(1973, Month.JULY, 12), "nellie.bloggs@test.net");
    students.add(s4);

    // Filter the list to only contain those born after 1975
    int yearOfBirthFilter = 1975;

    // Before J8 applying the filter required writing imperative code with external (in application code) iteration -
    List<Student> filteredStudents = new ArrayList<>(students.size());
    for (Student student : students) {
      if (student.getDob().getYear() > yearOfBirthFilter) {
        filteredStudents.add(student);
      }
    }
    assertThat(filteredStudents, contains(s2, s3));

    // From J8 onwards, the new Streams API can be used to apply the filter
    filteredStudents = students
        // Use Collection.stream() to create a java.util.stream.Stream (sequence of elements supporting sequential and
        // parallel aggregate operations) for operations on this Collection
        .stream()
        // Use Stream.filter() to filter the List of Students according to a java.util.function.Predicate, the
        // test() method of which is implemented as a Lambda expression
        .filter(s -> s.getDob().getYear() > yearOfBirthFilter)
        // Accumulate the elements of the stream back into a List
        // It's only when this terminal operation on the Stream is performed that the processing takes place.
        .collect(Collectors.toList());

    assertThat(filteredStudents, contains(s2, s3));
  }

  /**
   * Example use of {@link Stream#map(java.util.function.Function)} to 'map' a stream of elements (sourced from a
   * collection) of one type to a stream of elements of another type, declaratively, by applying a
   * {@link java.util.function.Function} to each individual element.
   */
  @Test
  public void testMap() {
    final List<Student> students = new ArrayList<>();
    final Student s1 = new Student(LocalDate.of(1974, Month.JUNE, 21), "joe.bloggs@test.net");
    students.add(s1);
    final Student s2 = new Student(LocalDate.of(1980, Month.JANUARY, 2), "jane.bloggs@test.net");
    students.add(s2);
    final Student s3 = new Student(LocalDate.of(1976, Month.AUGUST, 7), "jim.bloggs@test.net");
    students.add(s3);
    final Student s4 = new Student(LocalDate.of(1973, Month.JULY, 12), "nellie.bloggs@test.net");
    students.add(s4);

    // Create a list of all the student's email addresses
    // Uses a lambda expression to implement the Function passed to map(), using the method reference "::" syntax
    List<String> studentEmails = students.stream().map(Student::getEmail).collect(Collectors.toList());

    assertThat(studentEmails,
        contains("joe.bloggs@test.net", "jane.bloggs@test.net", "jim.bloggs@test.net", "nellie.bloggs@test.net"));
  }

  /**
   * Example use of {@link Stream#flatMap(java.util.function.Function)} which has the effect of applying a one-to-many
   * transformation on the elements of the stream (e.g. by retrieving a non-scalar property), and then flattening the
   * resulting elements into a new stream.
   */
  @Test
  public void testFlatMap() {
    final List<Student> students = new ArrayList<>();
    final Student s1 = new Student(LocalDate.of(1974, Month.JUNE, 21), "joe.bloggs@test.net");
    s1.addExamResult(new ExamResult("Maths", 75));
    s1.addExamResult(new ExamResult("Physics", 69));
    s1.addExamResult(new ExamResult("Chemistry", 84));
    students.add(s1);
    final Student s2 = new Student(LocalDate.of(1980, Month.JANUARY, 2), "jane.bloggs@test.net");
    s2.addExamResult(new ExamResult("English Literature", 87));
    s2.addExamResult(new ExamResult("History", 72));
    students.add(s2);

    // Create a list of all the student's exam results
    // Uses a lambda expression to implement the Function passed to flatMap()
    List<ExamResult> examResults = students.stream().flatMap(student -> student.getExamResults().stream()).collect(
        Collectors.toList());

    List<ExamResult> expextedExamResults = new ArrayList<>(s1.getExamResults());
    expextedExamResults.addAll(s2.getExamResults());
    assertThat(examResults, is(expextedExamResults));
  }

  /**
   * Example use of {@link Stream#distinct()} to remove duplicates from a sequence of elements (e.g. sourced from a
   * Collection).
   */
  @Test
  public void testDistinct() {
    final List<Student> students = new ArrayList<>();
    final Student s1 = new Student(LocalDate.of(1974, Month.JUNE, 21), "joe.bloggs@test.net");
    students.add(s1);
    final Student s2 = new Student(LocalDate.of(1980, Month.JANUARY, 2), "jane.bloggs@test.net");
    students.add(s2);
    final Student s3 = new Student(LocalDate.of(1976, Month.AUGUST, 7), "jim.bloggs@test.net");
    students.add(s3);
    // Add a duplicate Student to the list
    students.add(s2);
    final Student s4 = new Student(LocalDate.of(1973, Month.JULY, 12), "nellie.bloggs@test.net");
    students.add(s4);

    List<Student> uniqueStudents = students.stream().distinct().collect(Collectors.toList());

    assertThat(uniqueStudents, contains(s1, s2, s3, s4));
    assertThat(uniqueStudents, hasSize(students.size() - 1));
  }

  /**
   * Example use of {@link Stream#sorted()} and {@link Stream#sorted(java.util.Comparator)} to sort a sequence of
   * elements (e.g. sourced from a Collection) by their natural order, or a supplied Comparator.
   */
  @Test
  public void testSorted() {
    final List<Student> students = new ArrayList<>();
    final Student s1 = new Student(LocalDate.of(1974, Month.JUNE, 21), "joe.bloggs@test.net");
    final Student s2 = new Student(LocalDate.of(1980, Month.JANUARY, 2), "jane.bloggs@test.net");
    final Student s3 = new Student(LocalDate.of(1976, Month.AUGUST, 7), "jim.bloggs@test.net");
    final Student s4 = new Student(LocalDate.of(1973, Month.JULY, 12), "nellie.bloggs@test.net");
    students.add(s2);
    students.add(s1);
    students.add(s3);
    students.add(s4);

    List<Student> studentsSortedById = students.stream().sorted().collect(Collectors.toList());
    assertThat(studentsSortedById, contains(s1, s2, s3, s4));

    List<Student> studentsSortedByDob = students.stream().sorted(new Student.DobComparator()).collect(
        Collectors.toList());
    assertThat(studentsSortedByDob, contains(s4, s1, s3, s2));
  }

  /**
   * An example of how operations on {@link Stream} can be chained together. In this example the highest score that any
   * student was awarded for a particular subject in a particular graduation year is determineddeclaratively by chaining
   * operations on a Collection's stream.
   * <p>
   * Also illustrates the use of one of the classes of {@link Stream} used for primitive elements -
   * {@link java.util.stream.IntStream}, and the terminal reduction stream function
   * {@link java.util.stream.IntStream#max}.
   */
  @Test
  public void testChainStreamOperations() {
    final List<Student> students = new ArrayList<>();
    final Student s1 = new Student(LocalDate.of(1974, Month.JUNE, 21), "joe.bloggs@test.net");
    s1.setGraduationDate(LocalDate.parse("2014-09-12"));
    s1.addExamResult(new ExamResult("Maths", 75));
    s1.addExamResult(new ExamResult("Physics", 69));
    s1.addExamResult(new ExamResult("Chemistry", 84));
    students.add(s1);
    final Student s2 = new Student(LocalDate.of(1980, Month.JANUARY, 2), "jane.bloggs@test.net");
    s2.setGraduationDate(LocalDate.parse("2014-09-12"));
    s2.addExamResult(new ExamResult("English Literature", 87));
    s2.addExamResult(new ExamResult("History", 72)); // Didn't sit Math
    students.add(s2);
    final Student s3 = new Student(LocalDate.of(1974, Month.JUNE, 21), "jack.bloggs@test.net");
    s3.setGraduationDate(LocalDate.parse("2014-09-12"));
    s3.addExamResult(new ExamResult("Maths", 76)); // <-- The top Math score in 2014
    s3.addExamResult(new ExamResult("Geography", 75));
    students.add(s3);
    final Student s4 = new Student(LocalDate.of(1974, Month.JULY, 12), "jenny.bloggs@test.net");
    s4.setGraduationDate(LocalDate.parse("2013-09-12")); // Sat Math, but graduated a year earlier
    s4.addExamResult(new ExamResult("Chemistry", 65));
    s4.addExamResult(new ExamResult("Maths", 82));
    students.add(s4);

    //@formatter:off
    OptionalInt optionalInt = students.stream()
      .filter(s -> s.getGraduationDate().getYear() == 2014)
      .flatMap(s -> s.getExamResults().stream())
      .filter(er -> "Maths".equals(er.getExam()))
      .mapToInt(er -> er.getScore()) // Use specialised version of map function to get an IntStream
      .max(); // IntStream.max() is a short-cut for Stream.reduce(Integer::max)
    //@formatter:on   

    assertThat(optionalInt.isPresent(), is(true));
    assertThat(optionalInt.getAsInt(), is(76));
  }

  /**
   * Collections are not the only thing that can be used as the source of streams. The Streams API supports creating a
   * Stream from a supplied value or list of values aswell, using e.g. {@link Stream#of(Object...)}.
   */
  @Test
  public void testCreateStreamFromSuppliedValues() {
    String[] phrase = new String[] { "Everything", "comes", "to", "those", "who", "wait." };

    Stream<String> stream = Stream.of(phrase[0], phrase[1], phrase[2], phrase[3], phrase[4], phrase[5]);

    assertThat(stream.collect(Collectors.toList()), is(Arrays.asList(phrase)));
  }

  /**
   * Collections are not the only thing that can be used as the source of streams. {@link Arrays} provides various
   * methods for creating a stream from different types of arrays, including e.g. {@link Arrays#stream(Object[])}.
   */
  @Test
  public void testCreateStreamFromArray() {
    String[] phrase = new String[] { "Everything", "comes", "to", "those", "who", "wait." };

    Stream<String> stream = Arrays.stream(phrase);

    assertThat(stream.collect(Collectors.toList()), is(Arrays.asList(phrase)));
  }

  /**
   * Collections are not the only thing that can be used as the source of streams. {@link BufferedReader#lines} returns a
   * stream of all the lines in a Reader.
   *
   * @throws Exception If an unexpected error occurs.
   */
  @Test
  public void testCreateStreamFromBufferedReader() throws Exception {
    // Create a temp file containing multiple lines
    Path tempFile = Files.createTempFile(this.getClass().getCanonicalName(), ".tmp");
    tempFile.toFile().deleteOnExit();
    BufferedWriter writer = Files.newBufferedWriter(tempFile, StandardCharsets.UTF_8);
    String[] phrase = new String[] { "Everything", "comes", "to", "those", "who", "wait." };
    for (String word : phrase) {
      writer.write(word + System.lineSeparator());
    }
    writer.close();

    BufferedReader reader = Files.newBufferedReader(tempFile, StandardCharsets.UTF_8);
    // BufferedReader.lines() returns a stream containing lines in the Reader, these are only (lazily) read when the
    // terminal operation of the stream is executed - e.g. collect()
    List<String> readPhrase = reader.lines().collect(Collectors.toList());
    reader.close();

    assertThat(readPhrase, is(Arrays.asList(phrase)));
  }

  /**
   * Student domain object. Used to support these examples.
   */
  static class Student implements Comparable<Student> {
    private static int lastId;
    private final int id;
    private final String email;
    private final LocalDate dob;
    private LocalDate graduationDate;
    private final List<ExamResult> examResults;

    public Student(LocalDate dob, String email) {
      this.id = ++Student.lastId;
      this.dob = dob;
      this.email = email;
      this.examResults = new ArrayList<>();
    }

    public final int getId() {
      return this.id;
    }

    public final String getEmail() {
      return this.email;
    }

    public final LocalDate getDob() {
      return this.dob;
    }

    public final LocalDate getGraduationDate() {
      return graduationDate;
    }

    public final void setGraduationDate(LocalDate graduationDate) {
      this.graduationDate = graduationDate;
    }

    public void addExamResult(ExamResult examResult) {
      this.examResults.add(examResult);
    }

    public final List<ExamResult> getExamResults() {
      return examResults;
    }

    @Override
    public int compareTo(Student s) {
      return (this.id < s.getId()) ? -1 : (this.id == s.getId()) ? 0 : 1;
    }

    // auto-generated
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + id;
      return result;
    }

    // auto-generated
    @Override
    public boolean equals(Object obj) {
      if (this == obj) {
        return true;
      }
      if (obj == null) {
        return false;
      }
      if (!(obj instanceof Student)) {
        return false;
      }
      Student other = (Student) obj;
      if (id != other.id) {
        return false;
      }
      return true;
    }

    // auto-generated
    @Override
    public String toString() {
      StringBuilder builder = new StringBuilder();
      builder.append("Student [id=");
      builder.append(id);
      builder.append(", email=");
      builder.append(email);
      builder.append(", dob=");
      builder.append(dob);
      builder.append(", examResults=");
      builder.append(examResults);
      builder.append("]");
      return builder.toString();
    }

    static class DobComparator implements Comparator<Student> {
      @Override
      public int compare(Student s1, Student s2) {
        return s1.getDob().isBefore(s2.getDob()) ? -1 : s1.getDob().equals(s2.getDob()) ? 0 : 1;
      }
    }

  }

  static class ExamResult {
    private final String exam;
    private final int score;

    public ExamResult(String exam, int score) {
      this.exam = exam;
      this.score = score;
    }

    public final String getExam() {
      return this.exam;
    }

    public final int getScore() {
      return this.score;
    }

    // auto-generated
    @Override
    public String toString() {
      StringBuilder builder = new StringBuilder();
      builder.append("ExamResult [exam=");
      builder.append(exam);
      builder.append(", score=");
      builder.append(score);
      builder.append("]");
      return builder.toString();
    }
  }
}
TOP

Related Classes of com.neiljbrown.examples.java8.StreamApiExamplesTest$ExamResult

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.