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

优化以下代码

 import java.util.ArrayList; public class Test { public static void mainString args[] { ArrayList<String> list = new ArrayList<>; String prefix = "pre"; for int i = 0; i < 400; i++ { String s = prefix + i; list.adds; } System.out.printlnlist; } }

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

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

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

String s = prefix + i;
# 相当于
String s = new StringBuilder.appendprefix.appendi.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 mainjava.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:ILjava/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
}

优化后代码如下:

 import java.util.List; import java.util.ArrayList; public class Test { public static void mainString 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.appendi; list.addprefix.toString; prefix.deletebeforeAppLen, prefix.length; } System.out.printlnlist; } }

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

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

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

import java.util.ArrayList; public class Test { public static void mainString args[] { for int i = 0; i < 400; i++ { System.out.printlnprefix + i; } }
}

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

for int i = 0; i < 400; i++ { list.addprefix + i;
}

 ArrayList<String> list = new ArrayList<>400; char[] chars = new char[]{p,r,e,,,}; for int i = 0; i < 10; i++ { chars[3] = char i + 48; list.addnew Stringchars, 0, 4; } for int i = 10; i < 100; i++ { chars[4] = char i % 10 + 48; chars[3] = char i / 10 + 48; list.addnew Stringchars, 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.addnew Stringchars, 0, 6; } System.out.printlnlist;

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

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

 import java.util.ArrayList; public class Test { public static void mainString args[] { List<String> list = new ArrayList<String>400; final String prefix = "pre"; for int i = 0; i < 400; i++ { String s = prefix + i; list.adds; } System.out.printlnlist; } }

发表评论

电子邮件地址不会被公开。 必填项已用*标注