Java charAt() 还是子字符串?哪个更快?

发布于 2024-08-09 17:35:08 字数 360 浏览 4 评论 0原文

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

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)));}

最终结果需要是一个字符串。
那么你知道哪个会更快或更有效吗?

I want to go through each character in a String and pass each character of the String as a String to another function.

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

or

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

The final result needs to be a String.
So any idea which will be faster or more efficient?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

娇纵 2024-08-16 17:35:08

像往常一样:没关系,但如果您坚持花时间进行微优化,或者您真的喜欢针对非常特殊的用例进行优化,请尝试以下操作:

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());
    }

}

As usual: it doesn't matter but if you insist on spending time on micro-optimization or if you really like to optimize for your very special use case, try this:

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());
    }

}
巷子口的你 2024-08-16 17:35:08

答案是:没关系

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

The answer is: it doesn't matter.

Profile your code. Is this your bottleneck?

冰火雁神 2024-08-16 17:35:08

newFunction 真的需要采用 String 吗?如果您可以让 newFunction 接受一个 char 并像这样调用它,那就更好了:

newFunction(s.charAt(i));

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

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

Does newFunction really need to take a String? It would be better if you could make newFunction take a char and call it like this:

newFunction(s.charAt(i));

That way, you avoid creating a temporary String object.

To answer your question: It's hard to say which one is more efficient. In both examples, a String object has to be created which contains only one character. Which is more efficient depends on how exactly String.substring(...) and Character.toString(...) are implemented on your particular Java implementation. The only way to find it out is running your program through a profiler and seeing which version uses more CPU and/or more memory. Normally, you shouldn't worry about micro-optimizations like this - only spend time on this when you've discovered that this is the cause of a performance and/or memory problem.

猫九 2024-08-16 17:35:08

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

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

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 已经得到了很好的优化。

Of the two snippets you've posted, I wouldn't want to say. I'd agree with Will that it almost certainly is irrelevant in the overall performance of your code - and if it's not, you can just make the change and determine for yourself which is fastest for your data with your JVM on your hardware.

That said, it's likely that the second snippet would be better if you converted the String into a char array first, and then performed your iterations over the array. Doing it this way would perform the String overhead once only (converting to the array) instead of every call. Additionally, you could then pass the array directly to the String constructor with some indices, which is more efficient than taking a char out of an array to pass it individually (which then gets turned into a one character array):

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

But to reinforce my first point, when you look at what you're actually avoiding on each call of String.charAt() - it's two bounds checks, a (lazy) boolean OR, and an addition. This is not going to make any noticeable difference. Neither is the difference in the String constructors.

Essentially, both idioms are fine in terms of performance (neither is immediately obviously inefficient) so you should not spend any more time working on them unless a profiler shows that this takes up a large amount of your application's runtime. And even then you could almost certainly get more performance gains by restructuring your supporting code in this area (e.g. have newFunction take the whole string itself); java.lang.String is pretty well optimised by this point.

谁许谁一生繁华 2024-08-16 17:35:08

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

但我确实同意 Jesper 的观点,即最好只处理字符并避免所有 String 函数......

I would first obtain the underlying char[] from the source String using String.toCharArray() and then proceed to call newFunction.

But I do agree with Jesper that it would be best if you could just deal with characters and avoid all the String functions...

心房敞 2024-08-16 17:35:08

Leetcode 似乎更喜欢此处的子字符串选项。

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

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;
}

}

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

Leetcode seems to prefer the substring option here.

This is how I solved that problem:

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;
}

}

And this is the optimal solution they give:

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;
}

}

Honestly, I can't figure out why it would matter.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文