Merge pull request #12590 from etrandafir93/features/BAEL-5674-streams-multiple-vs-single-filters

BAEL-5674: added code examples for the article
This commit is contained in:
davidmartinezbarua 2022-08-16 15:55:52 -03:00 committed by GitHub
commit ff63982af2
5 changed files with 350 additions and 0 deletions

View File

@ -53,6 +53,12 @@
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh-generator.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -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;
}
}

View File

@ -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<Integer, Long> 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();
}
}

View File

@ -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<Student> 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<Student> 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<Student> 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<Student> filteredStream = students.stream()
.filter(Student::isEligibleForScholarship)
.collect(Collectors.toList());
assertThat(filteredStream).containsExactly(mathStudent);
}
}

View File

@ -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<Integer> 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<Integer> getMarks() {
return marks;
}
public void setMarks(List<Integer> marks) {
this.marks = marks;
}
public Profile getProfile() {
return profile;
}
public void setProfile(Profile profile) {
this.profile = profile;
}
}