if I have a List<T?> and want to filter elem...
# getting-started
m
if I have a List<T?> and want to filter elements such that I have a List<T> without null values and such that they satisfy a certain predicate, i could do
myList.filterNotNull().filter { f(it) }
, but that would essentially loop through the list twice, correct?
j
Yes, and not only loop twice but it will also create a whole intermediate
List
after the
filterNotNull
.
Usually it's not that big of a deal. But if you have very big collections or if you're measuring that this is a bottleneck for some reason, you can convert your list to a sequence in order to mitigate this. Or you can just use a single
filter
but that will not make the type of the elements non-null, which is annoying
m
So if I don't want that overhead, I need to do something like
filter { it != null && predicate(it) }
but I need to cast that to List<T>, because the compiler thinks it is still a List<T?>
j
Yeah I guess we wrote at the same time ๐Ÿ™‚ This is what I meant in my last option, yes. The first option would be to just be OK with the extra iteration/collection (seriously it might not be a problem at all). The second option would be using a sequence:
Copy code
myList.asSequence().filterNotNull().filter { predicate(it) }.toList()
Note that in general I extremely rarely encounter lists of nullable items. Maybe there is something you can do up front to avoid the nullable items in the first place?
m
I guess the sequence idea is best for my case; I don't think I can get rid of the nullable items unfortunately
Thanks a lot ๐Ÿ™‚
j
What collection sizes do you expect to handle here? Because your initial code might be OK
m
Its in a frameworky part of the codebase and rather generic, so I do neither know if the collections are large nor if the filtering is done in a tight loop
๐Ÿ‘Œ 1
m
You could always create your own method that does this. Look at the implementation of
filter
and
filterNotNull
, I don't think they are complicated.
๐Ÿ‘ 1
j
That's a fair point
j
Keep in mind, The difference between sequence and list here is minimal. There both going to run in O(n).
j
@Jacob Asymptotic execution time behaviour is not always the only thing that matters. Iterating once or 100 times over a given list can be a significant difference if it's big, and yet both are O(n). Creating an intermediate array list of 2M elements after the first
filterNotNull
might also be a significant difference. It's all about context / use case.
๐Ÿ‘ 2
m
@mkrussel They are really very readable, in fact, I started writing such an implementation before coming to this channel ๐Ÿ™‚
e
Copy code
myList.mapNotNull { if (it != null && f(it)) it else null }
a little contrivance results in a
List<T>
in a single pass, thanks to smart casting
m
myList.mapNotNull { it?.takeIf(f) }
๐Ÿ‘ 2
I think something like that would work.
e
either
f
or
::f
depending on context, but yes, that's equivalent