如何使用流过滤2D IntArray并将其映射到Set中

时间:2017-12-22 19:33:28

标签: arrays kotlin java-stream

我有一个2D IntArray代表一个游戏板,其中-1表示空格,而某些值更大或等于0意味着该单元属于某个玩家。像下面的东西(-1用点(。)表示)

. . . . . 
1 0 . . 2
0 . 1 3 3
4 3 . . 0

我希望获得一个Set,其中包含已被任何玩家占用的单元格位置。像这样:

[Cell{1,0}, Cell{1,1}, ..., Cell{3,4}]

我知道第一种方法是迭代2D数组:

val set = HashSet<Cell>();
for(row in 0 until HEIGHT){
    for (col in 0 until WIDTH){
        if(board[row][col] >= 0)
            set.add(Cell(row, col))
    }
}

但是......如果我使用流,它会以某种方式提高效率吗?它可以用更少的代码和更有效的方式实现吗?

IntStream.range(0, HEIGHT)
            .mapToObj { row -> IntStream.range(0, WIDTH)
                    .filter{ col -> board[row][col] >= 0}
                    .mapToObj { col -> Cell(row, col) } }
            .flatMap { point -> point }
            .collect(Collectors.toSet())

2 个答案:

答案 0 :(得分:1)

如果Java流被范围替换,可以使Kotlin更自然。

val cells = (0 until board.size).flatMap { row ->
    (0 until board[row].size)
            .filter { col -> board[row][col] >= 0 }.map { Cell(row, it) }
}.toSet()

答案 1 :(得分:1)

首先,建议使用Kotlin标准库而不是Java流。使用Kotlin标准库s1m0nw1 gave a good solution,我只会将flatMap替换为flatMapTo,以避免创建后来被丢弃的列表:

val set = HashSet<Cell>()
(0 until board.size).flatMapTo(set) { row ->
    (0 until board[row].size).filter { col ->
        board[row][col] >= 0
    }.map { col ->
        Cell(row, col)
    }
}

如果您确实需要Java8流解决方案,请使用以下代码:

fun IntRange.stream() : Stream<Int>
    = StreamSupport.stream(spliterator(), false)

val set = (0 until board.size).stream().flatMap { row ->
    (0 until board[row].size).stream().filter { col ->
        board[row][col] >= 0
    }.map { col ->
        Cell(row, col)
    }
}.collect(Collectors.toSet())

在性能方面,这里给出的两个解决方案应该非常相似(尽管我还没有测试过)。但是,Java8流允许您并行执行操作(请参阅StreamSupport.stream)。这是使用一些线程池,为您完成所有的魔术。据我所知,Kotlin并不提供开箱即用的任何并行操作,但它的Coroutines比Java线程快得多 - 但是你可以使用更多的代码。