11

我想遍历字符串中的每个字符并将字符串的每个字符作为字符串传递给另一个函数。

String s = "abcdefg";
for(int i = 0; i < s.length(); i++){
    newFunction(s.substring(i, i+1));}

或者

String s = "abcdefg";
for(int i = 0; i < s.length(); i++){
    newFunction(Character.toString(s.charAt(i)));}

最终结果需要是一个字符串。那么有什么想法会更快或更有效吗?

4

6 回答 6

15

像往常一样:没关系,但如果你坚持花时间在微优化上,或者如果你真的想为你非常特殊的用例进行优化,试试这个:

import org.junit.Assert;
import org.junit.Test;

public class StringCharTest {

    // Times:
    // 1. Initialization of "s" outside the loop
    // 2. Init of "s" inside the loop
    // 3. newFunction() actually checks the string length,
    // so the function will not be optimized away by the hotstop compiler

    @Test
    // Fastest: 237ms / 562ms / 2434ms
    public void testCacheStrings() throws Exception {
        // Cache all possible Char strings
        String[] char2string = new String[Character.MAX_VALUE];
        for (char i = Character.MIN_VALUE; i < Character.MAX_VALUE; i++) {
            char2string[i] = Character.toString(i);
        }

        for (int x = 0; x < 10000000; x++) {
            char[] s = "abcdefg".toCharArray();
            for (int i = 0; i < s.length; i++) {
                newFunction(char2string[s[i]]);
            }
        }
    }

    @Test
    // Fast: 1687ms / 1725ms / 3382ms
    public void testCharToString() throws Exception {
        for (int x = 0; x < 10000000; x++) {
            String s = "abcdefg";
            for (int i = 0; i < s.length(); i++) {
                // Fast: Creates new String objects, but does not copy an array
                newFunction(Character.toString(s.charAt(i)));
            }
        }
    }

    @Test
    // Very fast: 1331 ms/ 1414ms / 3190ms
    public void testSubstring() throws Exception {
        for (int x = 0; x < 10000000; x++) {
            String s = "abcdefg";
            for (int i = 0; i < s.length(); i++) {
                // The fastest! Reuses the internal char array
                newFunction(s.substring(i, i + 1));
            }
        }
    }

    @Test
    // Slowest: 2525ms / 2961ms / 4703ms
    public void testNewString() throws Exception {
        char[] value = new char[1];
        for (int x = 0; x < 10000000; x++) {
            char[] s = "abcdefg".toCharArray();
            for (int i = 0; i < s.length; i++) {
                value[0] = s[i];
                // Slow! Copies the array
                newFunction(new String(value));
            }
        }
    }

    private void newFunction(String string) {
        // Do something with the one-character string
        Assert.assertEquals(1, string.length());
    }

}
于 2009-11-04T09:06:07.227 回答
14

答案是:没关系

分析您的代码。这是你的瓶颈吗?

于 2009-11-04T08:47:31.603 回答
4

newFunction真的需要带吗StringnewFunction如果你可以采取 achar并这样称呼它会更好:

newFunction(s.charAt(i));

这样,您就可以避免创建临时 String 对象。

回答你的问题:很难说哪个更有效。在这两个示例中,String都必须创建一个仅包含一个字符的对象。哪个更有效取决于您在特定 Java 实现上的实现方式String.substring(...)和实现方式。Character.toString(...)找出它的唯一方法是通过分析器运行程序并查看哪个版本使用更多 CPU 和/或更多内存。通常,你不应该担心这样的微优化——只有当你发现这是性能和/或内存问题的原因时才花时间在这上面。

于 2009-11-04T08:43:23.687 回答
2

在您发布的两个片段中,我不想说。我同意 Will 的观点,几乎可以肯定它与代码的整体性能无关——如果不是,您可以进行更改并自己确定哪个对您的硬件上的 JVM 的数据最快。

也就是说,如果您先将 String 转换为 char 数组,然后在数组上执行迭代,则第二个片段可能会更好。这样做只会执行一次字符串开销(转换为数组),而不是每次调用。此外,您可以使用一些索引将数组直接传递给 String 构造函数,这比从数组中取出一个字符单独传递它(然后变成一个字符数组)更有效

String s = "abcdefg";
char[] chars = s.toCharArray();
for(int i = 0; i < chars.length; i++) {
    newFunction(String.valueOf(chars, i, 1));
}

但是为了加强我的第一点,当您查看每次调用时实际上要避免的内容时String.charAt()- 这是两个边界检查,一个(惰性)布尔 OR 和一个加法。这不会产生任何明显的差异。String 构造函数也没有区别。

从本质上讲,这两个习惯用法在性能方面都很好(两者都没有立即明显低效),因此您不应该再花时间处理它们,除非分析器显示这会占用大量应用程序的运行时间。即使这样,您也几乎可以肯定通过在该领域重组您的支持代码来获得更多的性能提升(例如,拥有newFunction整个字符串本身);java.lang.String 在这一点上已经得到了很好的优化。

于 2009-11-04T09:03:08.270 回答
0

我将首先使用 String.toCharArray() 从源字符串中获取底层 char[],然后继续调用 newFunction。

但我确实同意 Jesper 的观点,如果你可以只处理字符并避免所有 String 函数,那将是最好的......

于 2009-11-04T08:49:48.313 回答
0

Leetcode 似乎更喜欢这里的 substring 选项。

这就是我解决这个问题的方法:

class Solution {
public int strStr(String haystack, String needle) {
    if(needle.length() == 0) {
        return 0;
    }

    if(haystack.length() == 0) {
        return -1;
    }

    for(int i=0; i<=haystack.length()-needle.length(); i++) {
        int count = 0;
        for(int j=0; j<needle.length(); j++) {
            if(haystack.charAt(i+j) == needle.charAt(j)) {
                count++;
            }
        }
        if(count == needle.length()) {
            return i;
        }
    }
    return -1;
}

}

这是他们给出的最佳解决方案:

class Solution {
public int strStr(String haystack, String needle) {
    int length;
    int n=needle.length();
    int h=haystack.length();
    if(n==0)
        return 0;
    // if(n==h)
    //     length = h;
    // else
        length = h-n;
    if(h==n && haystack.charAt(0)!=needle.charAt(0))
            return -1;
    for(int i=0; i<=length; i++){
        if(haystack.substring(i, i+needle.length()).equals(needle))
            return i;
    }
    return -1;
}

}

老实说,我不明白为什么这很重要。

于 2019-09-23T19:16:21.007 回答