【问题标题】:Fastest way to remove rows in 2D Array删除二维数组中行的最快方法
【发布时间】:2015-04-08 16:52:46
【问题描述】:

我有一个程序需要从byte[][] 数组中删除一组行。我想删除其中没有零的每一行。现在我正在使用以下方法:

public byte[][] checkRows(byte[][] grid) {
    LinkedList<Integer> temp = new LinkedList<Integer>();
    boolean willRemove = false;
    for (int y = 0; y < grid.length; y++) {
        boolean tempCheck = true;
        for (int x = 0; x < grid[0].length; x++) {
            if (grid[y][x] == 0) {
                tempCheck = false;
                break;
            }
        }
        if (tempCheck) {
            temp.add(y);
            willRemove = true;
        }
    }

    if (willRemove) {
        int[] rowArray = convert(temp);
        return removeRows(grid, rowArray);
    }
    return grid;

}

public byte[][] removeRows(byte[][] grid, int[] rows) {
    int total = rows.length;
    int current = 0;
    byte[][] retGrid = new byte[grid.length][grid[0].length];
    for (int i = total; i < grid.length; i++) {
        if (current < total && i-total+current == rows[current]) {
            current++;
        }
        //retGrid[i] = grid[i-total+current].clone();
        System.arraycopy(grid[i-total+current], 0, retGrid[i], 0, xsize);

    }
    return retGrid;
}

public int[] convert(LinkedList<Integer> intList) {
    int[] retArray = new int[intList.size()];
    for (int i = 0; i < retArray.length; i++) {
        retArray[i] = intList.get(i).intValue();
    }
    return retArray;
}

这为我提供了一种从 2D 数组中删除行并用数组顶部的零行替换它们的相当快速的方法。有没有更快的方法来达到同样的效果?

如果不清楚脚本的作用,它是用于删除俄罗斯方块游戏中的完整行。

更新:使用 System.arraycopy() 代替 clone() 可为小型数组提供 5% 的性能提升。

【问题讨论】:

    标签: java arrays optimization arraylist clone


    【解决方案1】:

    使用链表会产生 O(1) 次删除,请参阅 this answer,因为无论如何都必须迭代列表。

    起初我认为 multidim 数组是紧凑的,因为它是一个连续的内存块,但it seems 并非如此。因此,您不会失去任何可能已经生效的缓存优势。

    可惜 Java 没有值类型(目前),我会使用一个而不是一个字节来编码信息。好吧,这不是绝对必要的……

    从代码审查的角度来看,在方法 checkRows 中使用 bool willRemove 是不必要的,因为在这些情况下,temp 将包含多个元素。如果不需要,我会尝试完全消除 ArrayList 分配 - 推迟它。

    【讨论】:

    • 您是说我应该将byte[][] 数组更改为LinkedList,还是您的意思是ArrayList?在我调用 check for rows to remove 的情况下,超过 90% 的情况下,不会删除任何行,这就是我添加检查的原因。由于我没有关于应该删除哪些行的任何信息,所以我使用 LinkedList(现在已更改)来跟踪要删除的行。
    • 我说的是byte[][],除了最后一段,它是关于temp 数组的。您必须分析该代码以确保一开始就存在瓶颈。如果您希望快速删除,请使用LinkedList。但是,如果您正在编写俄罗斯方块克隆,主要操作是插入完整图形的各个框。这不会遍历所有行;最有效的是 true 2D 数组 - Java 不提供。您可以使用一维数组并在其上添加一些索引操作。
    • 在这个阶段,实现LinkedList 而不是byte[][] 基本上意味着重写整个程序。而且由于我有在行和列上迭代的方法,除了删除之外,它会更有效吗,因为LinkedList.get()O(n)
    • 使用byte[][] 没有任何问题。无论哪种情况,迭代都是O(n),问题是随机访问,这比删除更经常需要。您可以封装实际表示的用法并将其包装在单独的类中;这将使您能够更改为LinkedList,而不需要程序的其余部分知道,并且您可以对其进行分析。
    • @maxb 如果有很多行,那么您可以使用LinkedList 更快地删除。但是对于大多数操作来说,它非常慢,所以无论如何你都可能会失败。使用数组并在一次扫描中进行删除,您可以(至少)与LinkedList 一样快。此外,您最好不要删除,只需重新排列(清理一整行并在顶部重复使用;请参阅我的答案)。
    【解决方案2】:

    恕我直言,这太复杂了。放弃LinkedList,因为无论如何这都是一件可怕的事情。不要费心收集要删除的索引,而是将要保留的每一行复制到另一个数组。然后用arraycopy覆盖原来的。

    您正在复制整行,但您不必这样做。只需重新排列它们,使保留的行落下并将完整的行移到顶部并清洁它们。无需分配内存。

    由于所有复制操作仅适用于一维,因此没有太多需要优化的地方,因为最耗时的操作可能是确定一行中是否有任何零并(不时)清理一些行。

    我想知道它在什么机器上运行,因为我猜它在最慢的手机上也一定非常快。顺便提一句。 CR 会更适合。想想更好的名字,例如,tempCheck -&gt; noZeroFound

    【讨论】:

    • 感谢您的回答!我正在创建一个玩俄罗斯方块的机器人,现在它每秒可以放下大约 700 块。考虑到它在做出决定之前会尝试各种方式来放置新的一块,checkRows() 会在每个块中执行很多次。在这个阶段,我正在尝试消除所有瓶颈。感谢您告诉我有关代码审查的信息!
    • 您的回答实际上帮助我优化了不少。不是通过删除 LinkedList(还),但我认为我可以限制填充行的位置,然后我只需要检查几行(
    • @maxb 我明白了,写一个机器人解释了优化需求。由于您不关心插槽是如何填充的,因此您可以切换到使用位并将整行打包到 int(或 long,如果最多 64 列)。这应该使一切变得更快。旋转一块变得棘手,但您可以简单地预先计算它。 CR 见。
    • 我本来打算去 CR,但你的建议只是把我最好的解决方案打成碎片,使整个程序快 3.5 倍。
    【解决方案3】:

    这个小方法针对一维案例执行问题的所需功能。

    private static final void onClearFilledRows(final byte[] pTetris, final int pRowLength) {
            int i = 0, j = 0;
            /* Track the last valid position of row data. */
            int lLastValidIndex = 0;
            /* Iterate through each row. */
            for(i = 0; i < pTetris.length; i += pRowLength) {
                /* Iterate through each cell in the row. */
                boolean lContainsZero = false;
                for(j = i; j < (i + pRowLength) & !lContainsZero; j++) {
                    lContainsZero |= pTetris[j] == 0;
                }
                /* This row is valid. Copy it to the last valid index. */
                if(lContainsZero) {
                    System.arraycopy(pTetris, i, pTetris, (lLastValidIndex++ * pRowLength), pRowLength);
                }
            }
            /* Empty the remaining rows. */
            for(i = lLastValidIndex * pRowLength; i < pTetris.length; i++) {
                /* Set the element to zero. */
                pTetris[i] = 0;
            }
        }
    

    然后可以针对二维情况重新设计此逻辑:

    private static final void onClearFilledRows(final byte[][] pTetris) {
            int i = 0, j = 0;
            /* Track the last valid position of row data. */
            int lLastValidIndex = 0;
            /* Iterate through each row. */
            for(i = 0; i < pTetris.length; i ++) {
                /* Iterate through each cell in the row. */
                boolean lContainsZero = false;
                for(j = 0; j < pTetris[i].length & !lContainsZero; j++) {
                    lContainsZero |= pTetris[i][j] == 0;
                }
                /* This row is valid. Copy it to the last valid index. */
                if(lContainsZero) {
                    System.arraycopy(pTetris[i], 0, pTetris[lLastValidIndex++], 0, pTetris[i].length);
                }
            }
            /* Empty the remaining rows. */
            for(i = lLastValidIndex; i < pTetris.length; i++) {
                for(j = 0; j < pTetris[i].length; j++) {
                    /* Set the element to zero. */
                    pTetris[i][j] = 0;
                }
            }
        }
    

    【讨论】:

    • 这似乎运作良好,只是新的零行被添加到数组的末尾,而不是开头。
    • 我刚刚对它进行了基准测试,它比我的第一次测试快了大约 5 倍。我编辑了该方法以倒数行数,并以正确的结果获得了相同的性能。
    • 当需要删除行时,您的方法似乎要快得多,但是当不需要删除行时,它会慢一些。很好的方法,但不幸的是,在>90% 的情况下,我没有删除任何行。
    • 太棒了!我考虑过这个。您可以添加一个参数,以便它只搜索到包含任何块的行;当其余为零时,您可以退出循环,无需运行/* Empty the remaining rows. */ 部分。
    • 您也只需要迭代到/* Iterate through each row */ 部分中的这一行,这是真正的开销所在。
    猜你喜欢
    • 2022-08-12
    • 1970-01-01
    • 2017-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-06
    相关资源
    最近更新 更多