优化以下代码
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);
}
}