Does anybody know of a nice way of creating a (non...
# announcements
r
Does anybody know of a nice way of creating a (non-mutable) list of items, combining single elements and a list of same elements? Example:
Copy code
val list = mutableListOf<Item>()
    .apply {
        add(getItem())
        addAll(getMultipleItems())
        add(getItem())
    }

fun getItem() = Item()
fun getMultipleItems() = listOf(Item(), Item(), Item())
☝️ this is the closest I've got
g
Copy code
val list: List<Item> = mutableListOf<Item()…
And done, it’s non-mutable now
w
Copy code
@Test
    fun asasTest1() {
        val list = List(10) { it }
        val result: MutableList<Int> = list + 99 //Compiling error
    }
    @Test
    fun asasTest2() {
        val mutableList: MutableList<Int> = mutableListOf<Int>(1, 2, 3)
        val result: List<Int> = mutableList + 99 //Compiles and it works
    }
r
sorry, I was unclear - mutability is not the problem, it's the combining of single elements and list of elements
aka how do I do this:
Copy code
val list  = listOf(
    1,
    2,
    listOf(3, 4, 5),
    6
)
w
Maybe with the
spread
operator?
Copy code
@Test
    fun asasTest() {
        val list = List(10) { it }
        val item1 = 90
        val item2 = 91
        val result: List<Int> = listOf(item1, item2, *(list.toTypedArray()))
    }
d
That's unfortunately quite inefficient, as two intermediary arrays will be created. It's a shame the spread operator is not supported on collections yet.
g
aka how do I do this
what is your real code, it’s not clear from this example. Version from your original sample is good IMO, not sure what exactly you want to improve The only efficient way is to create mutable list and add elements there, all other required creation of intermediate lists and copy of data
w
Yes, or can gather all "not already in a list" and make a list with those and do this:
Copy code
@Test
    fun asasTest() {
        val list = List(10) { it }
        val item1 = 90
        val item2 = 91
        val result: List<Int> = list + listOf(item1, item2)
    }
r
Yeah, as you guys mentioned, what I'm trying to avoid is the creation of extra unneeded lists. I'm just trying to assemble a list of items for a RecyclerView adapter. And most of them are unique, but one method returns multiple. I love the cleanliness and directness of listOf(1, 2, 3) (no intermediate mutable lists) and I hoped there is a similar way to mix items and collection of items
g
How do you expect it would work? Only if make it semi-dynamic using cast from Any, which sounds like not really nice idea, type safe way is to use intermediate lists on each operation. If you need efficient way, just use mutable list
r
This is the exact code I'm having
Copy code
val items: List<Item> = mutableListOf<Item?>()
    .apply {
        add(getHeader(it))
        add(getChangeDate(it))
        addAll(getProducts(it))
        add(getTotal(it))
        add(getCancelButton(it))
    }.filterNotNull()
g
looks completely fine for me, but creates copy of data on filterNotNull(), I would just check for nulls before adding them to collection
d
That looks like a reasonable solution. If you really need to optimize it further, you can e.g. use
getProducts(it).filterNotNullTo(this)
to directly filter the products and use null checks on the other values. But that's usually not worth it.
g
mutableListOf<Item?>()
->
mutableListOf<Item>()
you can just write extension fuction
addIfNonNull
2
r
Uuuu, I like this one, yes
g
and instead of
getProducts(it).filterNotNullTo(this)
use:
Copy code
getProducts(it).forEach { 
   if (it != null) add(it) // or use `addIfNonNull`
}
to avoid creation of additional list on filterNotNullTo
d
Why the forEach, Andrey?
filterNotNullTo does not create an additional collection
It's just a for loop inside
g
ahhh, you right, yeah
👍
r
Are you sure? It looks like a new collection:
Copy code
public fun <T : Any> Iterable<T?>.filterNotNull(): List<T> {
    return filterNotNullTo(ArrayList<T>())
}
g
it’s essentially the same as my code above, never used this extension tho
No, see, you need
filterNotNullTo
directly
r
Oh, right, and using this to use the same collection, cool
g
Copy code
val items: List<Item> = mutableListOf<Item>()
    .apply {
        addIfNonNull(getHeader(it))
        addIfNonNull(getChangeDate(it))
        getProducts(it).filterNotNullTo(this)
        addIfNonNull(getTotal(it))
        addIfNonNull(getCancelButton(it))
    }
1
something like that
d
The
filterNotNull
at the end is not need 😉
g
Copy-paste %)
🙃 1
also, to optimize it even further you can preallocate list size:
ArrayList<Item>(expectedCapacity)
instead of
mutableListOf<Item>()
p
There is an issue for container builders that addresses these issues. https://youtrack.jetbrains.com/issue/KT-15363
r
Thanks for the ideas guys, I went for this in the end, creating 2 small extension functions:
Copy code
val items: List<OrderModificationItem> = mutableListOf<OrderModificationItem>()
    .addNonNull(getOrderHeader(it))
    .addNonNull(getChangeDeliveryDate(it))
    .addAllNonNull(getProducts(it))
    .addNonNull(getOrderTotal(it))
    .addNonNull(getCancelOrderButton(it))
Copy code
private fun <E> MutableList<E>.addNonNull(e: E?): MutableList<E> {
    if (e != null) add(e)
    return this
}

private fun <E> MutableList<E>.addAllNonNull(list: List<E?>): MutableList<E> {
    list.forEach { addNonNull(it) }
    return this
}
g
Also preallocation will help in this case, you know max size of resulting list
r
Do you think it helps much? Getting the list Products in advance and using it to calculate the size will reverse the cleanliness we achieved so far
g
Not really, just small optimization, depends on case
👍 1