Collections in Java and Kotlin
Collections are groups of a variable number of items (possibly zero) that are significant to the problem being solved and are commonly operated on. This guide explains and compares collection concepts and operations in Java and Kotlin. It will help you migrate from Java to Kotlin and write your code in the authentically Kotlin way.
The first part of this guide contains a quick glossary of operations on the same collections in Java and Kotlin. It is divided into operations that are the same and operations that exist only in Kotlin. The second part of the guide, starting from Mutability, explains some of the differences by looking at specific cases.
For an introduction to collections, see the Collections overview or watch this video by Sebastian Aigner, Kotlin Developer Advocate.
Operations that are the same in Java and Kotlin
In Kotlin, there are many operations on collections that look exactly the same as their counterparts in Java.
Operations on lists, sets, queues, and deques
Description | Common operations | More Kotlin alternatives |
---|---|---|
Add an element or elements |
| Use the |
Check whether a collection contains an element or elements |
| Use the |
Check whether a collection is empty |
| Use |
Remove under a certain condition |
| |
Leave only selected elements |
| |
Remove all elements from a collection |
| |
Get a stream from a collection |
| Kotlin has its own way to process streams: sequences and methods like |
Get an iterator from a collection |
|
Operations on maps
Description | Common operations | More Kotlin alternatives |
---|---|---|
Add an element or elements |
| In Kotlin, the assignment |
Replace an element or elements |
| Use the indexing operator |
Get an element |
| Use the indexing operator to get an element: |
Check whether a map contains an element or elements |
| Use the |
Check whether a map is empty |
| Use |
Remove an element |
| Use the |
Remove all elements from a map |
| |
Get a stream from a map |
|
Operations that exist only for lists
Description | Common operations | More Kotlin alternatives |
---|---|---|
Get an index of an element |
| |
Get the last index of an element |
| |
Get an element |
| Use the indexing operator to get an element: |
Take a sublist |
| |
Replace an element or elements |
| Use the indexing operator instead of |
Operations that differ a bit
Operations on any collection type
Description | Java | Kotlin |
---|---|---|
Get a collection's size |
|
|
Get flat access to nested collection elements |
| |
Apply the given function to every element |
| |
Apply the provided operation to collection elements sequentially and return the accumulated result |
| |
Group elements by a classifier and count them |
| |
Filter by a condition |
| |
Check whether collection elements satisfy a condition |
| |
Sort elements |
| |
Take the first N elements |
| |
Take elements with a predicate |
| |
Skip the first N elements |
| |
Skip elements with a predicate |
| |
Build maps from collection elements and certain values associated with them |
|
To perform all of the operations listed above on maps, you first need to get an entrySet
of a map.
Operations on lists
Description | Java | Kotlin |
---|---|---|
Sort a list into natural order |
|
|
Sort a list into descending order |
|
|
Remove an element from a list |
|
|
Fill all elements of a list with a certain value |
| |
Get unique elements from a list |
|
Operations that don't exist in Java's standard library
zip()
,unzip()
– transform a collection.aggregate()
– group by a condition.takeLast()
,takeLastWhile()
,dropLast()
,dropLastWhile()
– take or drop elements by a predicate.slice()
,chunked()
,windowed()
– retrieve collection parts.Plus (
+
) and minus (-
) operators – add or remove elements.
If you want to take a deep dive into zip()
, chunked()
, windowed()
, and some other operations, watch this video by Sebastian Aigner about advanced collection operations in Kotlin:
Mutability
In Java, there are mutable collections:
Partially mutable ones:
And immutable ones:
If you write the last two pieces of code in IntelliJ IDEA, the IDE will warn you that you're trying to modify an immutable object. This code will compile and fail in runtime with UnsupportedOperationException
. You can't tell whether a collection is mutable by looking at its type.
Unlike in Java, in Kotlin you explicitly declare mutable or read-only collections depending on your needs. If you try to modify a read-only collection, the code won't compile:
Read more about immutability on the Kotlin coding conventions page.
Covariance
In Java, you can't pass a collection with a descendant type to a function that takes a collection of the ancestor type. For example, if Rectangle
extends Shape
, you can't pass a collection of Rectangle
elements to a function that takes a collection of Shape
elements. To make the code compilable, use the ? extends Shape
type so the function can take collections with any inheritors of Shape
:
In Kotlin, read-only collection types are covariant. This means that if a Rectangle
class inherits from the Shape
class, you can use the type List<Rectangle>
anywhere the List<Shape>
type is required. In other words, the collection types have the same subtyping relationship as the element types. Maps are covariant on the value type, but not on the key type. Mutable collections aren't covariant – this would lead to runtime failures.
Read more about collection types here.
Ranges and progressions
In Kotlin, you can create intervals using ranges. For example, Version(1, 11)..Version(1, 30)
includes all of the versions from 1.11
to 1.30
. You can check that your version is in the range by using the in
operator: Version(0, 9) in versionRange
.
In Java, you need to manually check whether a Version
fits both bounds:
In Kotlin, you operate with a range as a whole object. You don't need to create two variables and compare a Version
with them:
As soon as you need to exclude one of the bounds, like to check whether a version is greater than or equal to (>=
) the minimum version and less than (<
) the maximum version, these inclusive ranges won't help.
Comparison by several criteria
In Java, to compare objects by several criteria, you may use the comparing()
and thenComparingX()
functions from the Comparator
interface. For example, to compare people by their name and age:
In Kotlin, you just enumerate which fields you want to compare:
Sequences
In Java, you can generate a sequence of numbers this way:
In Kotlin, use sequences. Multi-step processing of sequences is executed lazily when possible – actual computing happens only when the result of the whole processing chain is requested.
Sequences may reduce the number of steps that are needed to perform some filtering operations. See the sequence processing example, which shows the difference between Iterable
and Sequence
.
Removal of elements from a list
In Java, the remove()
function accepts an index of an element to remove.
When removing an integer element, use the Integer.valueOf()
function as the argument for the remove()
function:
In Kotlin, there are two types of element removal: by index with removeAt()
and by value with remove()
.
Traverse a map
In Java, you can traverse a map via forEach
:
In Kotlin, use a for
loop or a forEach
, similar to Java's forEach
, to traverse a map:
Get the first and the last items of a possibly empty collection
In Java, you can safely get the first and the last items by checking the size of the collection and using indices:
You can also use the getFirst()
and getLast()
functions for Deque
and its inheritors:
In Kotlin, there are the special functions firstOrNull()
and lastOrNull()
. Using the Elvis operator
, you can perform further actions right away depending on the result of a function. For example, firstOrNull()
:
Create a set from a list
In Java, to create a Set
from a List
, you can use the Set.copyOf
function:
In Kotlin, use the function toSet()
:
Group elements
In Java, you can group elements with the Collectors function groupingBy()
:
In Kotlin, use the function groupBy()
:
Filter elements
In Java, to filter elements from a collection, you need to use the Stream API. The Stream API has intermediate
and terminal
operations. filter()
is an intermediate operation, which returns a stream. To receive a collection as the output, you need to use a terminal operation, like collect()
. For example, to leave only those pairs whose keys end with 1
and whose values are greater than 10
:
In Kotlin, filtering is built into collections, and filter()
returns the same collection type that was filtered. So, all you need to write is the filter()
and its predicate:
Learn more about filtering maps here.
Filter elements by type
In Java, to filter elements by type and perform actions on them, you need to check their types with the instanceof
operator and then do the type cast:
In Kotlin, you just call filterIsInstance<NEEDED_TYPE>()
on your collection, and the type cast is done by Smart casts:
Test predicates
Some tasks require you to check whether all, none, or any elements satisfy a condition. In Java, you can do all of these checks via the Stream API functions allMatch()
, noneMatch()
, and anyMatch()
:
In Kotlin, the extension functions none()
, any()
, and all()
are available for every Iterable object:
Learn more about test predicates.
Collection transformation operations
Zip elements
In Java, you can make pairs from elements with the same positions in two collections by iterating simultaneously over them:
If you want to do something more complex than just printing pairs of elements into the output, you can use Records. In the example above, the record would be record AnimalDescription(String animal, String color) {}
.
In Kotlin, use the zip()
function to do the same thing:
zip()
returns the List of Pair objects.
Associate elements
In Java, you can use the Stream API to associate elements with characteristics:
In Kotlin, use the associate()
function:
What's next?
Visit Kotlin Koans – complete exercises to learn Kotlin syntax. Each exercise is created as a failing unit test and your job is to make it pass.
Look through other Kotlin idioms.
Learn how to convert existing Java code to Kotlin with the Java to Kotlin converter.
Discover collections in Kotlin.
If you have a favorite idiom, we invite you to share it by sending a pull request.