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

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

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

我想要得到一个已经被任何玩家占用的单元格的位置。 像这样的东西:

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

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

 val set = HashSet(); 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()) 

首先,建议使用Kotlin标准库而不是Java流。 使用Kotlin标准库, s1m0nw1提供了一个很好的解决方案 ,我只用flatMapTo调用来替换flatMap ,以避免创建一个稍后被丢弃的列表:

 val set = HashSet() (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 = 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并不提供任何并行操作,但是它的协程比Java线程快很多,但是在您的端更多的代码会聚集在一起。

如果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()