如何最好地在嵌套列表中查找元素?

时间:2017-12-13 12:50:19

标签: functional-programming stream kotlin

Kotlin提供了一些有用的扩展功能,允许类似流的编程。

例如,如果我在列表中查找元素,我可以使用find

return list.find { n -> n>4 && n<6 }

但是当我有一个嵌套列表时,这似乎对我来说不实用。我使用forEach然后 - 幸运的是我可以从Kotlin的内部Lambda返回:

private fun findUsingForEach(data: List<List<Int>>, pred : (Int) -> Boolean) : Optional<Int> {
    data.forEach { list ->
        list.forEach { n ->
            if( pred(n) ) return Optional.of(n)
        }
    }
    return Optional.empty()
}

我认为forEach不是正确的工具。是否有更实用的方法来杜读这个?我想到了filter,但是嵌套会导致问题。

以下是我用于该功能的测试:

@Test
open fun findTest() {
    val data = listOf( listOf(1,2,3), listOf(3,4,5,6), listOf(), listOf(6,7,8) )
    val e = findUsingForEach( data, { n -> n>4 && n < 6 } )
    assertEquals(5, e.get())
}

2 个答案:

答案 0 :(得分:3)

你可以flatten列表:

  

fun <T> Iterable<Iterable<T>>.flatten(): List<T>source

     

返回给定集合中所有集合中所有元素的单个列表。

val data = listOf(listOf(1, 2, 3), listOf(3, 4, 5, 6), listOf(), listOf(6, 7, 8))
data.flatten().find { n -> n > 4 && n < 6 }

这将按顺序返回包含子列表元素的单个列表。然后你可以像往常一样使用find

在您的示例中,

{{1, 2, 3}, {3, 4, 5, 6}, {}, {6, 7, 8}}

变为

{1, 2, 3, 3, 4, 5, 6, 6, 7, 8}

此列表中find的结果为5

但是,这将创建一个新列表。看一下flatten的来源:

/**
 * Returns a single list of all elements from all collections in the given collection.
 */
public fun <T> Iterable<Iterable<T>>.flatten(): List<T> {
    val result = ArrayList<T>()
    for (element in this) {
        result.addAll(element)
    }
    return result
}

如果您想节省内存,请先从列表中创建Sequence

data.asSequence()

然后按此顺序执行操作:

data.asSequence().flatten().find { n -> n > 4 && n < 6 }

旁注:您的谓词n > 4 && n < 6仅相当于n == 5

答案 1 :(得分:1)

如果您只是想减少代码并且您不太关心效率,请尝试此操作。

list.flatten().find { your pred here }

或者

list.flatMap { it }.find { your pred }

或创建一个有用的实用程序,它不会创建新列表(更快/更低的内存):

inline fun <T> Iterable<Iterable<T>>.forEachEach(f: (T) -> Unit) =
    forEach { it.forEach(f) }