diff --git a/core-java-modules/core-java-streams-4/pom.xml b/core-java-modules/core-java-streams-4/pom.xml index 65de1d666c..ed4603796d 100644 --- a/core-java-modules/core-java-streams-4/pom.xml +++ b/core-java-modules/core-java-streams-4/pom.xml @@ -53,6 +53,12 @@ jmh-generator-annprocess ${jmh-generator.version} + + org.apache.commons + commons-lang3 + 3.12.0 + test + diff --git a/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/multiplefilters/MultipleFiltersVsComplexConditionFilterOrderUnitTest.java b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/multiplefilters/MultipleFiltersVsComplexConditionFilterOrderUnitTest.java new file mode 100644 index 0000000000..fcecc36631 --- /dev/null +++ b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/multiplefilters/MultipleFiltersVsComplexConditionFilterOrderUnitTest.java @@ -0,0 +1,89 @@ +package com.baeldung.streams.multiplefilters; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.IntStream; + +import org.junit.Before; +import org.junit.Test; + +public class MultipleFiltersVsComplexConditionFilterOrderUnitTest { + + AtomicInteger numberOfOperations = new AtomicInteger(); + + @Before + public void beforeEach() { + numberOfOperations.set(0); + } + + @Test + public void givenWrongFilterOrder_whenUsingMultipleFilters_shouldEvaluateManyConditions() { + long filteredStreamSize = IntStream.range(0, 100) + .boxed() + .filter(this::isEvenNumber) + .filter(this::isSmallerThanTwenty) + .count(); + + assertThat(filteredStreamSize).isEqualTo(10); + assertThat(numberOfOperations).hasValue(150); + } + + @Test + public void givenWrongFilterOrder_whenUsingSingleFilters_shouldEvaluateManyConditions() { + long filteredStreamSize = IntStream.range(0, 100) + .boxed() + .filter(i -> isEvenNumber(i) && isSmallerThanTwenty(i)) + .count(); + + assertThat(filteredStreamSize).isEqualTo(10); + assertThat(numberOfOperations).hasValue(150); + } + + @Test + public void givenCorrectFilterOrder_whenUsingMultipleFilters_shouldEvaluateFewerConditions() { + long filteredStreamSize = IntStream.range(0, 100) + .boxed() + .filter(this::isSmallerThanTwenty) + .filter(this::isEvenNumber) + .count(); + + assertThat(filteredStreamSize).isEqualTo(10); + assertThat(numberOfOperations).hasValue(120); + } + + @Test + public void givenCorrectFilterOrder_whenUsingHavingASlowCondition_shouldEvaluateFewerConditions() { + long filteredStreamSize = IntStream.range(0, 100) + .boxed() + .filter(this::isSmallerThanTwenty) + .filter(this::isEvenNumber) + .filter(this::verySlowFilter) + .count(); + + assertThat(filteredStreamSize).isEqualTo(10); + assertThat(numberOfOperations).hasValue(130); + } + + private boolean isEvenNumber(int i) { + numberOfOperations.incrementAndGet(); + return i % 2 == 0; + } + + private boolean isSmallerThanTwenty(int i) { + numberOfOperations.incrementAndGet(); + return i < 20; + } + + private boolean verySlowFilter(int i) { + numberOfOperations.incrementAndGet(); + // commented the Thread.sleep() not to slow down the pipeline. + // try { + // Thread.sleep(1000); + // } catch (InterruptedException e) { + // throw new RuntimeException(e); + // } + return true; + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/multiplefilters/MultipleFiltersVsComplexConditionFilterPerformanceIntegrationTest.java b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/multiplefilters/MultipleFiltersVsComplexConditionFilterPerformanceIntegrationTest.java new file mode 100644 index 0000000000..1362431b07 --- /dev/null +++ b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/multiplefilters/MultipleFiltersVsComplexConditionFilterPerformanceIntegrationTest.java @@ -0,0 +1,130 @@ +package com.baeldung.streams.multiplefilters; + +import java.text.MessageFormat; +import java.util.function.Function; +import java.util.stream.IntStream; + +import org.apache.commons.lang3.time.StopWatch; +import org.junit.Test; + +public class MultipleFiltersVsComplexConditionFilterPerformanceIntegrationTest { + +// this test is slow, avoid running it on pipeline + @Test + public void measureProcessingTimeForEachSolution() { + + averageMultipleMeasurements("Stream with Multiple Filters", this::measureTimeForMultipleFilters, 10_000); + averageMultipleMeasurements("Stream with Multiple Filters", this::measureTimeForMultipleFilters, 100_000); + averageMultipleMeasurements("Stream with Multiple Filters", this::measureTimeForMultipleFilters, 10_00_000); + averageMultipleMeasurements("Stream with Multiple Filters", this::measureTimeForMultipleFilters, 1_0000_000); + + averageMultipleMeasurements("Stream with Complex Condition", this::measureTimeForComplexCondition, 10_000); + averageMultipleMeasurements("Stream with Complex Condition", this::measureTimeForComplexCondition, 100_000); + averageMultipleMeasurements("Stream with Complex Condition", this::measureTimeForComplexCondition, 10_00_000); + averageMultipleMeasurements("Stream with Complex Condition", this::measureTimeForComplexCondition, 1_0000_000); + + averageMultipleMeasurements("Parallel Stream with Multiple Filters", this::measureTimeForParallelStreamWithMultipleFilters, 10_000); + averageMultipleMeasurements("Parallel Stream with Multiple Filters", this::measureTimeForParallelStreamWithMultipleFilters, 100_000); + averageMultipleMeasurements("Parallel Stream with Multiple Filters", this::measureTimeForParallelStreamWithMultipleFilters, 10_00_000); + averageMultipleMeasurements("Parallel Stream with Multiple Filters", this::measureTimeForParallelStreamWithMultipleFilters, 1_0000_000); + + averageMultipleMeasurements("Parallel Stream with Complex Condition", this::measureTimeForParallelStreamWithComplexCondition, 10_000); + averageMultipleMeasurements("Parallel Stream with Complex Condition", this::measureTimeForParallelStreamWithComplexCondition, 100_000); + averageMultipleMeasurements("Parallel Stream with Complex Condition", this::measureTimeForParallelStreamWithComplexCondition, 10_00_000); + averageMultipleMeasurements("Parallel Stream with Complex Condition", this::measureTimeForParallelStreamWithComplexCondition, 1_0000_000); + + averageMultipleMeasurements("Old For Loop with Complex Condition", this::measureTimeForOlfForLoopWithComplexCondition, 10_000); + averageMultipleMeasurements("Old For Loop with Complex Condition", this::measureTimeForOlfForLoopWithComplexCondition, 100_000); + averageMultipleMeasurements("Old For Loop with Complex Condition", this::measureTimeForOlfForLoopWithComplexCondition, 10_00_000); + averageMultipleMeasurements("Old For Loop with Complex Condition", this::measureTimeForOlfForLoopWithComplexCondition, 1_0000_000); + + } + + private void averageMultipleMeasurements(String measurementName, Function measurement, int range) { + double avgTime = IntStream.range(0, 100) + .mapToLong(i -> measurement.apply(range)) + .average() + .orElseThrow(); + + System.out.println(MessageFormat.format("{0}; Stream size: {1}; Processing Time (ms): {2}", measurementName, range, avgTime)); + } + + public long measureTimeForMultipleFilters(int range) { + StopWatch watch = new StopWatch(); + watch.start(); + + IntStream.range(0, range) + .boxed() + .filter(i -> i != 10) + .filter(i -> i != 20) + .filter(i -> i != 30) + .filter(i -> i != 40) + .filter(i -> i != 50) + .filter(i -> i != 60) + .count(); + + watch.stop(); + return watch.getTime(); + } + + public long measureTimeForParallelStreamWithMultipleFilters(int range) { + StopWatch watch = new StopWatch(); + watch.start(); + + IntStream.range(0, range) + .boxed() + .parallel() + .filter(i -> i != 10) + .filter(i -> i != 20) + .filter(i -> i != 30) + .filter(i -> i != 40) + .filter(i -> i != 50) + .filter(i -> i != 60) + .count(); + + watch.stop(); + return watch.getTime(); + } + + public long measureTimeForComplexCondition(int range) { + StopWatch watch = new StopWatch(); + watch.start(); + + IntStream.range(0, range) + .boxed() + .filter(i -> i != 10 && i != 20 && i != 30 && i != 40 && i != 50 && i != 60) + .count(); + + watch.stop(); + return watch.getTime(); + } + + public long measureTimeForParallelStreamWithComplexCondition(int range) { + StopWatch watch = new StopWatch(); + watch.start(); + + IntStream.range(0, range) + .boxed() + .parallel() + .filter(i -> i != 10 && i != 20 && i != 30 && i != 40 && i != 50 && i != 60) + .count(); + + watch.stop(); + return watch.getTime(); + } + + public long measureTimeForOlfForLoopWithComplexCondition(int range) { + StopWatch watch = new StopWatch(); + watch.start(); + + int count = 0; + for (int i = 0; i <= range; i++) { + if (i != 10 && i != 20 && i != 30 && i != 40 && i != 50 && i != 60) { + count++; + } + } + + watch.stop(); + return watch.getTime(); + } +} \ No newline at end of file diff --git a/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/multiplefilters/MultipleFiltersVsComplexConditionReadabilityUnitTest.java b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/multiplefilters/MultipleFiltersVsComplexConditionReadabilityUnitTest.java new file mode 100644 index 0000000000..80fdb8a30a --- /dev/null +++ b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/multiplefilters/MultipleFiltersVsComplexConditionReadabilityUnitTest.java @@ -0,0 +1,65 @@ +package com.baeldung.streams.multiplefilters; + +import static java.util.function.Predicate.not; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.Before; +import org.junit.Test; + +public class MultipleFiltersVsComplexConditionReadabilityUnitTest { + + private List students; + private Student mathStudent; + + @Before + public void beforeEach() { + this.mathStudent = new Student(); + mathStudent.setName("John Doe"); + mathStudent.setYear(3); + mathStudent.setProfile(Student.Profile.MATH); + mathStudent.setMarks(List.of(80, 90, 77, 95, 100)); + + Student csStudent = new Student(); + csStudent.setName("Paul Atreides"); + csStudent.setYear(2); + csStudent.setProfile(Student.Profile.COMPUTER_SCIENCE); + csStudent.setMarks(List.of(30, 40, 60)); + + this.students = List.of(mathStudent, csStudent); + } + + @Test + public void whenUsingMultipleFilters_dataShouldBeFiltered() { + List filteredStream = students.stream() + .filter(s -> s.getMarksAverage() > 50) + .filter(s -> s.getMarks().size() > 3) + .filter(not(s -> s.getProfile() == Student.Profile.PHYSICS)) + .collect(Collectors.toList()); + + assertThat(filteredStream).containsExactly(mathStudent); + } + + @Test + public void whenUsingSingleComplexFilter_dataShouldBeFiltered() { + List filteredStream = students.stream() + .filter(s -> s.getMarksAverage() > 50 + && s.getMarks().size() > 3 + && s.getProfile() != Student.Profile.PHYSICS) + .collect(Collectors.toList()); + + assertThat(filteredStream).containsExactly(mathStudent); + } + + @Test + public void whenUsingSingleComplexFilterExtracted_dataShouldBeFiltered() { + List filteredStream = students.stream() + .filter(Student::isEligibleForScholarship) + .collect(Collectors.toList()); + + assertThat(filteredStream).containsExactly(mathStudent); + } + +} \ No newline at end of file diff --git a/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/multiplefilters/Student.java b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/multiplefilters/Student.java new file mode 100644 index 0000000000..0e6b77ae2c --- /dev/null +++ b/core-java-modules/core-java-streams-4/src/test/java/com/baeldung/streams/multiplefilters/Student.java @@ -0,0 +1,60 @@ +package com.baeldung.streams.multiplefilters; + +import java.util.List; +import java.util.stream.Collectors; + +public class Student { + + private String name; + private int year; + private List marks; + private Profile profile; + + public enum Profile { + COMPUTER_SCIENCE, + MATH, + PHYSICS + } + + public double getMarksAverage() { + return marks.stream().collect(Collectors.averagingInt(m -> m)); + } + + public boolean isEligibleForScholarship() { + return getMarksAverage() > 50 + && marks.size() > 3 + && profile != Profile.PHYSICS; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getYear() { + return year; + } + + public void setYear(int year) { + this.year = year; + } + + public List getMarks() { + return marks; + } + + public void setMarks(List marks) { + this.marks = marks; + } + + public Profile getProfile() { + return profile; + } + + public void setProfile(Profile profile) { + this.profile = profile; + } +} \ No newline at end of file