Is String.replace implementation really efficient?

后端 未结 3 887
青春惊慌失措
青春惊慌失措 2021-02-13 03:55

I used to think that String.replace is faster than String.replaceAll because the latter uses Pattern regex and the former does not. But in fact there is no significant differenc

相关标签:
3条回答
  • 2021-02-13 04:31

    String replace and replace all method there is no diffrence in effiency or time both take n operation to replace the charecter following code may help you how string replace method work in java you can checkout in your eclipse

    package com.jav.exec;
    
    public class replace {
    
        static String s1="This the India the india the nation";
    
        public static String replace(String s2,String s3)
        {
            int counter=0;
            int count=s3.length()-1;
            int count1=0;
            char[] ca1=s1.toCharArray();
            char[] ca2=s2.toCharArray();
            char[] ca3=s3.toCharArray();
    
            for(int f1=0;f1<ca1.length;f1++)//finding the number of occurances
            {
                if(ca1[f1]==ca2[counter])//increasing counter if charecters matches
                {
                    counter++;
    
                    if(counter==s2.length())//if consecutive charecters makes count equal to replacing string
                    {
                        count1++;//increasing the count if consecutive charecters matches the striing
                        counter=0;//making counter 0 for next occurence
                    }
                }
                else
                {
                    counter=0;//making counter 0 if occurence less than the string 
                }
            }
    
            char[] ca4=new char[ca1.length+(count1*ca3.length)-(count1*ca2.length)];
            //creating new sized array for storing values
            counter=0;
            count=s3.length()-1;
            count1=0;
            int count2=0;
    
            for(int f=0;f<ca4.length;f++)
            {
                ca4[f]=ca1[count2];//storing the values into the new array of original long array
                //increasing the count  
                if(ca1[count2]==ca2[counter])//if charecters matches replacing array charecters
                {
                    counter++;
                    if(counter==ca2.length)//if counter is equal to length of replacing array
                    {
                        f=f+ca3.length-ca2.length;//increasing the count of f
                        for(int f1=0;f1<ca3.length;f1++)//storing replaced array charecters to new array
                        {
                            ca4[f-count]=ca3[count1];
                            count1++;
                            count--;
                        }
                        counter=0;//redeclaring variables to their inital values for next replacement
                        count1=0;
                        count=s3.length()-1;
                    }
                }
    
                else
                {   
                    while(counter>0)
                    {   
                        ca4[f-counter]=ca1[count2-counter];
                        counter--;
                    }
                }
                count2++;
            }
            return new String(ca4);
        }
    
        public static void main(String[] args)
        {
            System.out.println(replace("the","mother"));
        }
    }
    
    0 讨论(0)
  • 2021-02-13 04:35

    There is one interesting aspect when comparing the two solutions, at least on my machine. The built-in version scales much better when it comes to larger strings. Given a slightly modified version of your test:

    for (int i = 0; i < 10; i++) {
        s1 = s1 + s1;
        long t0 = call1(s1); // your implementation
        long t1 = call2(s1); // 1.7_07 Oracle
        long delta = t0 - t1;
    
        System.out.println(
          String.format("Iteration %s, string length %s, call1 %s, call2 %s, delta %s", i, s1.length(), t0, t1, delta));
    
        try {
            Thread.sleep(200);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    

    By just doubling the string length with each call, the break-even is reached already after iteration 3 or 4:

    Iteration 0, string length 22, call1 450, call2 1715, delta -1265
    Iteration 1, string length 44, call1 1048, call2 2152, delta -1104
    Iteration 2, string length 88, call1 2695, call2 4024, delta -1329
    Iteration 3, string length 176, call1 7737, call2 7574, delta 163
    Iteration 4, string length 352, call1 24662, call2 15560, delta 9102
    

    For reference the two implementations of call1 and call2:

    static long call1(String s) {
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            String s2 = replace(s, "11", "22");
        }
        return System.currentTimeMillis() - t0;
    }
    
    static long call2(String s) {
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            String s2 = s.replace("11", "xxx");
        }
        return System.currentTimeMillis() - t0;
    }
    
    0 讨论(0)
  • 2021-02-13 04:37

    To give you some idea how inefficient String.replace is

    From the source for Java 7 update 11.

    public String replace(CharSequence target, CharSequence replacement) {
        return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
            this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
    }
    

    AFAIK, the use of a Pattern and Matcher.quiteReplacement etc is an attempt to be clear rather than efficient. I suspect it dates back to when many internal libraries were written without performance considerations.

    IMHO Java 7 has seen many internal libraries improve performance, in particular reduce needless object creation. This method is an obvious candidate for improvement.


    You can improve the performance by doing the copy once, instead of trying to insert into an existing StringBuilder.

    static String replace2(String s, String target, String replacement) {
        StringBuilder sb = null;
        int start = 0;
        for (int i; (i = s.indexOf(target, start)) != -1; ) {
            if (sb == null) sb = new StringBuilder();
            sb.append(s, start, i);
            sb.append(replacement);
            start = i + target.length();
        }
        if (sb == null) return s;
        sb.append(s, start, s.length());
        return sb.toString();
    }
    
    public static void main(String... ignored) {
        String s1 = "11112233211";
        for (; ; ) {
            timeReplace(s1);
            timeReplace2(s1);
            timeStringReplaceRefactored(s1);
            timeStringReplace(s1);
        }
    }
    
    private static void timeStringReplace(String s1) {
        long start0 = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            String s2 = s1.replace("11", "xxx");
            if (s2.length() <= s1.length()) throw new AssertionError();
        }
        System.out.printf("String.replace %,d ns avg%n", System.currentTimeMillis() - start0);
    }
    
    private static void timeStringReplaceRefactored(String s1) {
        long start0 = System.currentTimeMillis();
        Pattern compile = Pattern.compile("11", Pattern.LITERAL);
        String xxx = Matcher.quoteReplacement("xxx");
        for (int i = 0; i < 1000000; i++) {
            String s2 = compile.matcher(s1).replaceAll(xxx);
            if (s2.length() <= s1.length()) throw new AssertionError();
        }
        System.out.printf("String.replace %,d ns avg (Refactored)%n", System.currentTimeMillis() - start0);
    }
    private static void timeReplace(String s1) {
        long start0 = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            String s2 = replace(s1, "11", "xxx");
            if (s2.length() <= s1.length()) throw new AssertionError();
        }
        System.out.printf("Replace %,d ns avg%n", System.currentTimeMillis() - start0);
    }
    
    private static void timeReplace2(String s1) {
        long start0 = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            String s2 = replace2(s1, "11", "xxx");
            if (s2.length() <= s1.length()) throw new AssertionError();
        }
        System.out.printf("My replace %,d ns avg%n", System.currentTimeMillis() - start0);
    }
    
    static String replace(String s, String target, String replacement) {
        StringBuilder sb = new StringBuilder(s);
        for (int i = 0; (i = sb.indexOf(target, i)) != -1; i += replacement.length()) {
            sb.replace(i, i + target.length(), replacement);
        }
        return sb.toString();
    }
    

    prints

    Replace 177 ns avg
    My replace 108 ns avg
    String.replace 436 ns avg (Refactored)
    String.replace 598 ns avg
    

    Catching the Pattern and replace text helps a little, but not as much as having a custom routine to do the replace.

    0 讨论(0)
提交回复
热议问题