Chapter 8 - Java for Beginners Course

Filtering a List

In this section we’ll introduce the example that will serve us as guide for the remainder of the chapter.

Number filtering

Assume that we have a List<Integer> with some elements and that we need to filter these data and only return numbers that are below a certain maximum (let’s assume 10 is our limit), like this:

In this example we are using Integer, but the same applies to any other type of object.
In the example code run the JavaListExampleApp
// our input list of numbers
List<Integer> numbers = Arrays.asList(1, 2, 3, 10, 11, 12, 0);

// we'll store our filtered results in this list
List<Integer> filtered = new ArrayList<>();

// filter the list and allow only numbers that are less than 10
for (Integer number : numbers) {
    if (number < 10) {
        filtered.add(number);
    }
}

// print out the filtered list
for (Integer number : filtered) {
    System.out.println(number);
}

Output:

1
2
3
0

In this example our filter is inside the first for loop, where we check for each number in our list whether it passes or not the criteria we have, in our case, we only allow numbers that are less than 10.

Changing the filter

Now, what happens if we’re asked to implement multiple filters? For example, what if we also need to filter the list to allow only numbers greater than 5?

We could of course, change our filter to be:

//...
for (Integer number : numbers) {
    if (number > 5) {
        filtered.add(number);
    }
}
//...

However, this isn’t very generic and depending the case it might not be what we want to do.

For example, what happens if we need to filter using logic that we don’t control? Like in the case where we provide a code library and the users of our library want to define how to filter the data.

This now sounds like we’re defining a contract between us and others, and of course, the easiest approach to do this is to provide an interface that defines this contract of what a Filter is.

In our case, a filter receives an element (in our example an Integer) and it should return true if the element should be included, and false otherwise. This interface can be modelled like this:

public interface Filter<E> {
    boolean filter(E element);
}

Greater than 5 filter

Now, for the second filter mentioned above, we can define an implementation of our Filter class that filters numbers and only allows numbers greater than 5, as such:

public class GreaterThanFiveFilter implements Filter<Integer> {

    @Override
    public boolean filter(Integer element) {
        if (element > 5) {
            return true;
        }
        return false;
    }

}

Filtering numbers using a Filter

Once we have the implementation of Filter that we want to use, we can change our original code to use it, for example:

In the example code run the JavaListExampleWithFilterApp
public static void main(String[] args) {
    // our input list of numbers
    List<Integer> numbers = Arrays.asList(1, 2, 3, 10, 11, 12, 0);

    // get the filter we want to use
    Filter<Integer> filterToUse = new GreaterThanFiveFilter();

    // call our new `filterNumbers` static method to do the filtering
    List<Integer> filtered = filterNumbers(numbers, filterToUse);

    // print out the filtered list
    for (Integer number : filtered) {
        System.out.println(number);
    }
}

private static List<Integer> filterNumbers(List<Integer> numbers, Filter<Integer> filterToUse) {
    List<Integer> filtered = new ArrayList<>();
    // filter the list using the provided `filterToUse`
    for (Integer number : numbers) {
        if (filterToUse.filter(number)) {
            filtered.add(number);
        }
    }
    return filtered;
}

Output:

10
11
12

The good and the bad

The good thing with our new approach is that we now have a new method called filterNumbers that receives the list of numbers we want to filter and it can also receive any implementation of our Filter interface to perform the filtering.

In this sense, we can provide any type of filter we want (like, finding numbers lower than, greater than or equal to a given number, or anything we want really).

The bad thing is that we need to create a whole new class per filter we want to apply. This is very verbose, for example, we already have a GreaterThanFiveFilter class, and if we want to implement other filters, like in the first example above, we’d need to create more classes.

This is one place where anonymous classes and, more recently, lambdas can help as we’ll see in the next 2 sections.

This of course isn’t really a major problem, but it isn’t great either. Our example is very basic and only meant to introduce the concepts in the next sections. This is to highlight the issue we’re trying to solve.
Code in GitHub

Get the code for this tutorial using the links below.

Project Repo
Download code for this step
Main class for this step
Dependencies

This is a list of recommended tutorials or courses that might be useful before starting this one.

Contents
Welcome to the Course!
Course Introduction
Chapter 1 - Building Blocks
Quick introduction to Java Variables Classes And Objects Class Example - Defining a class Object Examples - Creating instances Java Application Example - Running our first app Accessing class members - The dot operator Packages - Organizing the code
Chapter 2 - Primitives and Operators
Primitives Arithmetic Operators Assignment Operator Unary Operators Equality and Relational Operators Conditional Operators
Chapter 3 - Statements and Control Flow
Expressions Statements If-Then Statement If-Then-Else Statement More If Statements Switch Statement While and Do-While Statements For Statement Branching Statements Exception Handling
Chapter 4 - Code Example
Example Project - A Simple Vending Machine Adding money Delivering Items Giving Change
Chapter 5 - Classes and Interfaces
Introduction Access Level Modifiers Class Declaration - Class, Methods and Fields Class Declaration - Constructors Inheritance Basics Inheritance - Constructors Inheritance - Methods and Fields Polymorphism Abstract Classes and Methods Interfaces Static Class Members Class Composition Final Classes and Class Members Generic Classes
Chapter 6 - Base Object Behaviors
Introduction Type Comparison Type Casting Object Equality - The Contract Object Equality - Common Pitfalls Object String Representation Garbage Collection Object Comparison Primitive Wrappers and Autoboxing
Chapter 7 - Data Structures
Introduction Arrays - Declaration and Creation Arrays - Basic Operations Core Collection Interfaces List and ArrayList - Basic Operations ArrayList Internals Introduction to Hash Tables Map and HashMap - Basic Operations Set and HashSet - Basic Operations
Chapter 8 - Anonymous classes and lambdas
Introduction Filtering a List Anonymous Classes Lambdas Built-in Functional Interfaces
Chapter 9 - Streams
Introduction Creating Streams Intermediate Operations Terminal Operations