Kotlin List API — Part 1: List Filtering

A straightforward explanation of Kotlin’s List.filter method

dashfwd
Kotlin Thursdays
5 min readApr 25, 2019

--

Resources

Introduction

In the process of learning Kotlin I started like most people probably do: I coded like a Java developer. I wasn’t familiar with Haskell or Erlang, so functional programming was new to me. I knew Java had some functional features, but I rarely used them.

I knew of methods like filter and sortedBy and knew that map and flatMap were a thing, but the I found the official API lacking.

In this series of articles, my goal is to highlight important List API methods that use functional programming techniques, but I may cover others as well.

In this first article, I will cover the the List.filter method, first by implementing it myself, and then by using the actual API method.

Hi folks! If the text is blurry, please make sure the playback quality is set to 1080p!

Sample Data/Requirements

To explain how filter works, let’s start with some sample data using Kotlin’s data class. The sample data will consist of a List of three People.

Sample Data

Requirements

For the purposes of this example, let’s assume we need to filter a list of people to include only those people over the age of 20.

Let’s Implement Filtering (like it’s 2004)

Rather than use the actual List.filter implementation in the Kotlin API, I’m going to implement it myself as an exercise to better understand the underlying function of filter.

Instead of using a lot of modern language features, I’m going to pretend like it’s 2004 and implement it as simply as I can. If I were implementing this today, I’d use Lambdas, Generics and Extensions, and a lot of other language features, but for the purposes of the example I’m keeping things as simple as possible.

Listing 1: The 2004 filter implementation

Let’s examine the implementation of this method.

  1. Line 1 : The function is defined, with the input being a List<People> and the output also being a List<People>.
  2. Line 2: A new list is created which will hold the return value, which will be the filtered list (note in Kotlin we’d probably use a mutableList instead of an ArrayList but remember, it’s 2004 and we’re going retro here).
  3. Line 3 and 7: Loop through each Person in the list, and assign each to the person variable.
  4. Line 4 : Evaluate the criteria; in this case that the person’s age is greater than 20.
  5. Line 5: Add the person to the filteredList.
  6. Line 8: Return the filteredList

Next, I’ll call the overTwenty function passing in the people list as a parameter:

The over20 list would include Zander (age 78) and Ali (age 35) but not Fred (age 12).

That works… but it’s pretty inelegant and inflexible. Time to try out Kotlin’s List.filter method, and see how that differs.

Let’s implement it (using List.filter)

Now let’s use List.filter rather than implementing it ourselves. The implementation looks like this:

Let’s break down this call:

  1. val over20: a variable to hold the result of filtering; a List<People>
  2. people: the list being filtered; also a List<People>.
  3. filter: the method name.
  4. { } : the brackets indicate a Lambda (a function) being passed as a parameter to the filter method.
  5. it.age > 20 : the condition that defines how to filter elements.
    - The variable it is the implicit name of the looping variable; in other words, it’s the Person instance from the person List.
    - This function should return true for elements to be included in the resulting List, or false for elements that should be excluded.

Let’s look at the advantages of List.filter over the 2004 implementation:

  1. It’s much more concise (one line vs 10).
  2. No need to iterate the List using forEach.
  3. No need to create the intermediate List and add elements to it.
  4. Works with any type of List (not just List<Person>).
  5. Because the criteria is a (lambda) parameter, you can swap out the filter criteria and do things like people.filter { it.age < 50 && it.name == "Fred"} or people.filter { it.name == “Bill”}. Rather than coding for a specific criteria, you can pass any criteria in as a lambda.

Certainly a great improvement.

Here’s the full code comparing the “2004” implementation vs using List.filter in case you want to run it yourself.

Other Methods for Filtering Lists

In addition to List.filter, there are a number of other filtering methods that have specialized purposes.

filterNot

The filterNot method works just like the regular filter method, except instead of filtering elements that match the condition in the lambda, it filters things that do not match.

filterIsInstance

The filterIsInstance method filters a List to contain only those elements that are instances of a particular class.

filterNotNull

The filterNotNull method returns a List that has all null values filtered out. Let’s do another example.

One place where this is useful is when you’re using a List that was provided to you by Java code, which includes elements which may or may not be null. In Kotlin such a List might be represented as List<String!>!, which is a List that may or may not null, and which contains elements that may or may not be null. (The exclamation point indicates a platform type) .

The resulting noNullsHere variable will be of type List<String>.

filterTo, filterNotTo, filterIsInstanceTo, filterNotNullTo

In all the previous examples, the methods create a new List and put the filtered elements in it. If you’d prefer to put the filtered items into an existing list, there’s the “To” methods, which take the “destination” List as the first parameter of the method call.

Summary (tl;dr)

Here are the key points of how filter method works:

  • The input and output of the filter method is a list of a particular type: List<E>
  • The filter method takes a lambda as a parameter that returns true for elements that should be included in the returned list, or false for elements that should not be included.
  • An example of usage is below:
    val filteredList = inputList.filter { it.age > 20 }
  • In addition to the normal filter method, there are other methods that filter things that don’t match a condition (filterNot) , elements that are of a particular type (filterIsInstance), elements that are not null (filterNotNull) and some “To” methods that allow you to provide your own List to add to, rather than the default behavior of having a List created for you.

Thanks for reading! I’ll see you soon for Part 2, where I will cover the List methods any, all, and none.

Questions, feedback? Find me on Twitter.

--

--