首页 > 一道朋友java面试的优化题 ,不得解

一道朋友java面试的优化题 ,不得解

优化以下代码

 import java.util.ArrayList;

    public class Test {
    
        public static void main(String args[]) {
            ArrayList<String> list = new ArrayList<>();
    
            String prefix = "pre";
    
            for (int i = 0; i < 400; i++) {
                String s = prefix + i;
                list.add(s);
            }
            System.out.println(list);
        }
    }

不太清楚出题者的意图? String的问题?貌似是,但是不知道怎么改


        ArrayList<String> list = new ArrayList<>(400);
        char[] chars = new char[]{'p','r','e','\0','\0','\0'};
        for (int i = 0; i < 10; i++) {
            chars[3] = (char) (i + 48);
            list.add(new String(chars, 0, 4));
        }
        for (int i = 10; i < 100; i++) {
            chars[4] = (char) ((i % 10) + 48);
            chars[3] = (char) ((i / 10) + 48);
            list.add(new String(chars, 0, 5));
        }
        for (int i = 100; i < 400; i++) {
            chars[5] = (char) ((i % 10) + 48);
            chars[4] = (char) (((i / 10) % 10) + 48);
            chars[3] = (char) ((i / 100) + 48);
            list.add(new String(chars, 0, 6));
        }
        System.out.println(list);

仅考虑性能的话 不管怎么变着花的用StringBuilder都不如亲自操作数组来的高效,不过需要手动转换数字
对于这道题来说 循环四百次 用一个长度为6的char数组即可满足要求
对于ArrayList动态扩容也是需要时间的,所以初始化时把长度写申请足了也会起一点作用。。


import java.util.ArrayList;

public class Test {
    public static void main(String args[]) {
        for (int i = 0; i < 400; i++) {
            System.out.println(prefix + i);
        }
    }
}

优化点应该是减少for循环中产生的对象

for (int i = 0; i < 400; i++) {
    list.add(prefix + i);
}

优化后代码如下:

    import java.util.List;
    import java.util.ArrayList;

    public class Test {
    
        public static void main(String args[]) {
        
            List<String> list = new ArrayList<String>();
    
            StringBuilder prefix = new StringBuilder("pre");
            int max = 400;
            for (int i = 0; i < max; i++) {
                int beforeAppLen = prefix.length();
                prefix.append(i);
                list.add(prefix.toString());
                prefix.delete(beforeAppLen, prefix.length());
            }
            System.out.println(list);
            
        }
    }

1、ArrayList 改为 List,体现继承的地方。

2、优化 String,改为使用 StringBuilder,性能优化。

3、提取局部变量400,使用有意义的变量命名,可读性问题。


--------- 后来更新
如果代码是 String s = prefix + i;,那么我下面说的优化方法是错误的,不适用于这种情况。
我说的只有在 prefix = prefix + i;的情况下才成立。

这段代码的主要问题是
java compiler 会用 StringBuilder 来进行字符串连接,在循环中会有不必要的重复操作。

String s = prefix + i;
# 相当于
String s = new StringBuilder().append(prefix).append(i).toString();

上面的循环在执行过程中会重复new 400 个 StringBuilder();
要优化的话把 new StringBuilder() 提取到局部变量即可。

见字节码注释:

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
       7: astore_1
       # 分配字符串常量'pre'
       8: ldc           #4                  // String pre
       # 存放到局部变量2中
      10: astore_2
      11: iconst_0
      12: istore_3
      # 取出i的值压栈
      13: iload_3
      # 循环结束条件压栈
      14: sipush        400
      # 弹栈,判断是否结束循环,如果满足,跳转到标号53执行,结束循环
      17: if_icmpge     53
      # 下面几条指令在利用 StringBuilder 拼接字符串,并调用 toString 方法得到结果
      20: new           #5                  // class java/lang/StringBuilder
      23: dup
      24: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
      # 局部变量2的值压栈,就是prefix的值
      27: aload_2
      # 调用append,弹出刚才压入的局部变量2,作为方法参数
      28: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      # 局部变量3的值压栈,就是i的值
      31: iload_3
      # 调用append,弹出刚才压入的局部变量3,作为方法参数
      32: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      35: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      38: astore        4
      40: aload_1
      41: aload         4
      43: invokevirtual #10                 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
      46: pop
      # 变量i加1
      47: iinc          3, 1
      # 加 1 后跳转到标号13,继续执行下一次循环
      50: goto          13
      53: getstatic     #11                 // Field java/lang/System.out:Ljava/io/PrintStream;
      56: aload_1
      57: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      60: return
}

理论上以下通过以下两处更改可以优化,但我试了下指定初始化list容量,性能反倒降低。很是纳闷,还是请高手解答吧。

 import java.util.ArrayList;

    public class Test {
    
        public static void main(String args[]) {
            List<String> list = new ArrayList<String>(400);
    
            final String prefix = "pre";
    
            for (int i = 0; i < 400; i++) {
                String s = prefix + i;
                list.add(s);
            }
            System.out.println(list);
        }
    }
【热门文章】
【热门文章】