Java - 什么可以使这段代码运行得更快?

时间:2011-10-15 23:39:16

标签: java android performance sqlite

我在Objective-c中实现了几乎相同的代码,运行速度比Java快两到三倍。我正在试图找出哪些指令可能是最耗费资源的,并且看看是否有更好的方法来做同样的事情,这在Java中更有效。

这是从数据库中读取大型结果集的例程的一部分,对于返回的每个单词,它会检查该单词是否可以由玩家具有的字母区块构成。它包括对空白图块的支持,可以用作任何字母。空白图块将以下划线字符表示。

基本上,对于从数据库返回的每个单词,我遍历单词的每个字母,并查看可用字母的玩家数组。如果我找到那封信,我将其从玩家阵列中移除并继续前进。如果我没有找到该字母,则该字被丢弃并且下一个字被读取。除非,我在播放器的数组中找到一个下划线字符,然后,我将使用它作为字母,并将其从数组中删除。如果我到达数据库字的字母数组的末尾并且每个字母都“找到”,那么该单词将保存在列表中。

我已经计时整个函数的各个部分,数据库查询发生得非常快。这只是对这个游标的处理非常慢。任何建议将不胜感激!

if (c.moveToFirst()) {

    do { 
        boolean found = false;
        int aValue = 0;
        int letterValue = 0;

        // Word and Word's length from the database
        String sWord = c.getString(0);
        int wordLength = c.getInt(1);

        // Refresh the Tile array, underscores sorted to the front
        // sortedTiles sorted the players tiles {_,_,a,b,c}
        char[] aTiles = sortedTiles.clone();

        // Calculate the value of the word
        for (int i = 0; i < wordLength; i++) {

            // For each character in the database word
            switch (sWord.charAt(i)) {
            case 97:
                letterValue = 1;
                break;
            case 98:
                letterValue = 4;
                break;
            case 99:
                letterValue = 4;
                break;
            case 100:
                letterValue = 2;
                break;
            case 101:
                letterValue = 1;
                break;
            case 102:
                letterValue = 4;
                break;
            case 103:
                letterValue = 3;
                break;
            case 104:
                letterValue = 3;
                break;
            case 105:
                letterValue = 1;
                break;
            case 106:
                letterValue = 10;
                break;
            case 107:
                letterValue = 5;
                break;
            case 108:
                letterValue = 2;
                break;
            case 109:
                letterValue = 4;
                break;
            case 110:
                letterValue = 2;
                break;
            case 111:
                letterValue = 1;
                break;
            case 112:
                letterValue = 4;
                break;
            case 113:
                letterValue = 10;
                break;
            case 114:
                letterValue = 1;
                break;
            case 115:
                letterValue = 1;
                break;
            case 116:
                letterValue = 1;
                break;
            case 117:
                letterValue = 2;
                break;
            case 118:
                letterValue = 5;
                break;
            case 119:
                letterValue = 4;
                break;
            case 120:
                letterValue = 8;
                break;
            case 121:
                letterValue = 3;
                break;
            case 122:
                letterValue = 10;
                break;
            default:
                letterValue = 0;
                break;
            } // switch

            found = false;

            // Underscores will be sorted to the front of the array, 
            // so start from the back so that we give
            // real letters the first chance to be removed.
            for (int j = aTiles.length - 1; j > -1; j--) {
                if (aTiles[j] == sWord.charAt(i)) {
                    found = true;
                    // Increment the value of the word
                    aValue += letterValue;

                    // Blank out the player's tile so it is not reused
                    aTiles[j] = " ".charAt(0);

                    // I was removing the element from the array
                    // but I thought that might add overhead, so
                    // I switched to just blanking that letter out
                    // so that it wont be used again
                    //aTiles = removeItem(aTiles, j);

                    break;
                }

                if (aTiles[j] == cUnderscore) {
                    found = true;

                    // Blank out the player's tile so it is not reused
                    aTiles[j] = " ".charAt(0);

                    // I was removing the element from the array
                    // but I thought that might add overhead, so
                    // I switched to just blanking that letter out
                    // so that it wont be used again
                    //aTiles = removeItem(aTiles, j);
                    break;
                }

            } // for j

            // If a letter in the word could not be fill by a tile 
            // or underscore, the word doesn't qualify
            if (found == false) {
                break;
            }

        } // for i

        // If all the words letters were found, save the value and add to the list.
        if (found == true) {

            // if all the tiles were used it's worth extra points
            String temp = aTiles.toString().trim();

            if (temp.length() < 1) {
                aValue += 35;
            }

            Word word = new Word();
            word.word = sWord;
            word.length = wordLength;
            word.value = aValue;
            listOfWords.add(word);
        }

    } while (c.moveToNext());
}

4 个答案:

答案 0 :(得分:8)

我不知道您的代码在大部分时间内的确切位置。你应该描述一下。但我会用表查找替换你的long switch语句:

// In the class:
private static final int[] letterValues = {
    1, 4, 4, 2, 1, // etc.
}

// In the code:

// Calculate the value of the word
for (int i = 0; i < wordLength; i++) {

    // For each character in the database word
    char ch = sWord.charAt(i);
    if (ch >= 97 && ch <= 122) {
        letterValue = letterValues[ch - 97];
    } else {
        letterValue = 0;
    }

    // the rest of your code

这可能比switch语句快得多。

编辑:我注意到,在j循环中,您为每个sWord.charAt(i)值调用了j。通过将函数调用分解出循环,可以加快速度。如果您使用我的代码,则可以使用ch代替sWord.charAt(i)

P.S。作为一种风格问题,编码if (found) { ...代替if (found == true) { ...更好。同样,请使用if (!found) {代替if (found == false) {

答案 1 :(得分:1)

我认为switch语句可能会被编译器变成跳转表,所以我没有看到它的问题。

另一方面,您可以为玩家的手使用更好的数据结构。现在,你基本上使用了一个三重嵌套循环:

  1. 迭代数据库中的每个单词
  2. 迭代单词中的每个字符
  3. 迭代播放器磁贴阵列中的每个角色
  4. 前两个是无法避免的。另一方面,您可以为第三个使用哈希表或某种O(N)查找数据结构。

    我可能会将手牌表示为27个整数的数组。每个元素代表一个字母或“_”,其值是手中的瓦片数。找到匹配的图块时,可以减小其值。如果该值已经为零,那么您就知道该播放器没有该磁贴。

    但正如泰德指出的那样,最好的办法是使用分析器找到最昂贵的电话。然后弄清楚如何尽可能少地进行这些调用。

答案 2 :(得分:1)

你倾向于得到猜测的答案。

要做的是,在每个平台上,只需squeeze the code until it's optimal。 然后,如果有任何速度差异,至少你会知道每个代码都尽可能快。

分析是经常推荐的,但是here's an example of how I do it

答案 3 :(得分:0)

谢谢大家。我当时希望得到电子邮件提醒,所以,我没有意识到人们已经在回复。

我最终在光标循环的不同位置放置系统时间的日志打印输出,以尝试确定花费最多时间的内容。最初的moveToFirst()占用了大部分时间,但这是可以预料的,我真的无能为力。我注意到大多数单词在两三毫秒内处理,这应该足够快,但是,由于没有明显的原因,一个单词需要20或30毫秒。我推断在后台必须进行一些垃圾收集,因此,我试图尽可能地减少分配/解除分配。在所有的循环中,而不是声明变量,如(int i = 0 ... to for(i = 0,将变量声明移动到方法的顶部。这有点帮助,但我仍然离开一条线未触及。当我改变它时,它在世界上造就了一切。

    // Refresh the Tile array, underscores sorted to the front

    for (i = 0; i < sortedTiles.length; i++) {
        aTiles[i] = sortedTiles[i];
    }

    // Instead of doing it this way                 
    //aTiles = sortedTiles.clone();

我在游标循环上方分配了一个Tile,在这里,我只是用玩家手中的角色重新初始化它。这一个clone()是必要的频繁垃圾收集,并且杀死了我的表现。

您已经提供了很好的建议,我会进一步调整代码,根据您的建议提高效果。非常感谢你!