In this Q&A we'll over a few options to filter ArrayList, their differences and provide benchmark performance metrics.
Filtering Options
Java provides multiple options to filter List elements. Options include
- Using for loop, iterate and filter List elements
- Using List.forEach
- Using streams ( Java 8 and above).
- Using plain old iterator
- Using List.removeIf
- Using List.removeIf
Performance
Which one is the best performing? Executing code below yields the following results.
BenchMark summary with 1 sec execution interval
Benchmark Avg
Filter.filterWithFor 3.139
Filter.filterWithForEach 5.238
Filter.filterWithIterator 3.282
Filter.filterWithStreamAndCollect 3.989
Filter.filterWithStreamForEach 5.941
Average performance of external iterators is marginally better for the given ArrayList. One has to benchmark specific use cases to make a decision.
You can use a parallel stream to improve performance of stream iteration.
Processing Order
For a ArrayList, stream by default maintains the processing order. There's no difference unless you use the stream.unordered().
Filtering in place
All the options above involve creating a new List. To filter in place use collection.removeIf to remove unwanted list elements. See filterInPlace function below
Deleting elements in the middle of an Array is not good from performance. You can use LinkedList.removeIf() to filter to reduce the .
POM Dependency
Add the jmh-core and jmh-generator-annprocess artificats to POM to run the code
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark )
//@Fork(value = 2, jvmArgs = {"-Xms2G", "-Xmx2G"})
@Warmup( time=1, iterations = 3, timeUnit = TimeUnit.MILLISECONDS )
@Measurement(time=1, timeUnit = TimeUnit.MILLISECONDS, iterations = 5)
public class Filter {
private final List<Integer> l = new ArrayList<>(1000000);
private final List<Integer> l1 = new LinkedList<>();
@Setup()
public void setup() {
System.out.println( "In setup");
for(int i=0; i < 1000000; ++i) {
//l.add(i);
l1.add(i);
}
}
@Benchmark
public List<Integer> filterWithFor() {
List<Integer> l2 = new ArrayList<>();
for(Integer i : l) {
if( i <= 200 && i >= 100 )
l2.add(i);
}
return l2;
}
@Benchmark
public List<Integer> filterWithForEach() {
List<Integer> l2 = new ArrayList<>();
l.forEach( (x) -> { if(x <= 200 && x >= 100 ) l2.add(x); } );
return l2;
}
@Benchmark
public List<Integer> filterWithStreamForEach() {
List<Integer> l2 = new ArrayList<>();
l.stream()
.filter( (Integer x) -> x <= 200 && x >= 100 )
.forEach( (x) -> l2.add(x));
return l2;
}
@Benchmark
public List<Integer> filterWithStreamAndCollect() {
List<Integer> l2 = l.stream()
.filter( (Integer x) -> x <= 200 && x >= 100 )
.collect( Collectors.toList());
return l2;
}
@Benchmark
public List<Integer> filterWithIterator() {
List<Integer> l2 = new ArrayList<>();
ListIterator<Integer> li = l.listIterator();
while(li.hasNext()) {
Integer i = li.next();
if( i <= 200 && i >= 100 )
l2.add(i);
}
//Collections.unmodifiableCollection();
return l2;
}
@Benchmark
public List<Integer> filterInPlace() {
l.removeIf( (i) -> i <= 200 && i >= 100 );
return l;
}
@Benchmark
public List<Integer> filterLLInPlace() {
l1.removeIf( (i) -> i <= 200 && i >= 100 );
return l;
}
public static void main( String[] args ) throws RunnerException {
Options opt = new OptionsBuilder()
.include(Filter.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
}
Comments
Post a Comment