Skip to main content

How do I filter ArrayList

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

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