Search code examples
kotlinapache-storm

Kotlin: looping through a filtered (possibly empty and at most one element) list


I have a storm bolt that gets tuples which contain a value of type MyClass, which in turn carries a time field startTime. At the end of the bolt, I'd like to log the time that the processing took from the beginning (marked by startTime). So I could do:

override fun doExecute(input: Tuple): Boolean {
    ... // my processing logic
    input.values.filterIsInstance(MyClass).forEach {
        val endToEndTime = (System.currentTimeMillis() - it.startTime).toInt()
        stats.recordExecutionTime("mytag", endToEndTime)
    }
} 

I don't really have a problem with this approach. Just cosmetic wise it looks like I could have more than one instance of MyClass in the tuple (which I do not). So I was thinking if I could just pick the first out of the list like:

override fun doExecute(input: Tuple): Boolean {
    ... // my processing logic
    input.values.filterIsInstance(MyClass).first().let {
        val endToEndTime = (System.currentTimeMillis() - it.startTime).toInt()
        stats.recordExecutionTime("mytag", endToEndTime)
    }
} 

This seems to be more readable. However, if the filterIsInstance(MyClass) call returns an empty list, I get an exception by calling first on it, while the first solution goes through fine without any exception. Since this is for pure logging purposes, I'd like it to be exception-free.

Is there a better way to do this?


Solution

  • There is a better way! :)

    You can use firstOrNull() and then a safe call:

    input.values.filterIsInstance(MyClass).firstOrNull()?.let {
        val endToEndTime = (System.currentTimeMillis() - it.startTime).toInt()
        stats.recordExecutionTime("mytag", endToEndTime)
    }
    

    You could also replace the filterIsInstance call with the version of firstOrNull() that takes a predicate, like so:

    input.values.firstOrNull { it is MyClass }?.let {ű
        it as MyClass
        val endToEndTime = (System.currentTimeMillis() - it.startTime).toInt()
        stats.recordExecutionTime("mytag", endToEndTime)
    }