Authors Top

If you have a few years of experience with the Kotlin language and server-side development, and you’re interested in sharing that experience with the community, have a look at our Contribution Guidelines.

1. Introduction

Kotlin’s standard library is quite comprehensive. Despite being based on Java standard library, it contains a lot of extension functions for which Java lacks suitable substitutes.

One set of functions particularly useful in everyday engineering is aggregation functions for collections. Such functions allow us to reduce a collection to a single value.

More specifically, any iterable or array of primitive types has its shortcut collectors.

In this tutorial, we’re going to look at one of them – the sum() function.

2. Sum Aggregation Function

There are several sum() functions in the Kotlin standard library: one extension function on the Iterable interface for each of the numeric primitive types and then one for each of the arrays of primitive numeric types. Inside they are all very similar:

public fun Iterable<Int>.sum(): Int {
    var sum: Int = 0
    for (element in this) {
        sum += element
    }
    return sum
}

As we can see, this function will work even on an empty collection, returning 0:

assert(listOf<Int>().sum() == 0) // true

And it’ll give a proper sum:

listOf(
    16, 96, 100, 15, 30,
    70, 11, 31, 52, 70,
    42, 50, 56, 93, 57
).sum()
  .also { assertEquals(789, it) }

Since this function works only on lists of integers, there is no possibility of a null sneaking somewhere. That means that this function never throws any exceptions.

In fact, it doesn’t even throw if Int type overflows:

listOf(Int.MAX_VALUE - 2, 1, 2, 4)
  .sum()
  .also { assert(-2147483644 == it) } // true

So we need to watch our data closely and whenever list.average() * list.size > Int.MAX_VALUE, we must convert the whole list to Long or accept the loss of precision and use Double.

If our collection is enormously huge (much bigger than O(100000)), it might be beneficial to split it into several chunks and process it in parallel:

val executor = Executors.newFixedThreadPool(4)
listOf(16, 96, 100, 15, 30, 70, 11, 31, 52, 70, 42, 50, 56, 93, 57)
  .asSequence()
  .chunked(chunkSize) // Must be calculated based on the collection size and the number of available CPU cores
  .map { executor.submit(Callable { it.sum() }) }
  .map { it.get() }
  .sum()
  .also { assertEquals(789, it) }

Since our task is heavily CPU-bound, there is no point in using coroutines here. A simple thread pool with tasks will be enough.

However, it must be noted that on modern CPUs, the arithmetic operations are extremely fast, and we are unlikely to reap any benefits except in very extreme cases.

3. Conclusion

In this article, we described one of the Kotlin standard library collection functions, sum(). It is always a good idea to check if Kotlin library already has a function we need before writing our own. Chances are that it’s exactly the case.

The sum() function works on any collection or array of a primitive type, returning an Int for Byte, Short, and Int and the same type as the list for Long, Double, and Float. There are no checks for overflow in the algorithm, so we make an implicit assumption that the sum of the elements is going to fit into the result type.

All code examples are available over on GitHub.

Authors Bottom

If you have a few years of experience with the Kotlin language and server-side development, and you’re interested in sharing that experience with the community, have a look at our Contribution Guidelines.

Comments are closed on this article!