java中的字符串8内存较少

时间:2015-11-29 10:28:18

标签: string memory java-8 stringbuilder memory-efficient

我得到了以下代码,我被问到哪个选项有以下模式:

XXXX-XXXX-XXXX-2324

...

以下代码:

public class CCMark {

    public static String maskCC(String creditCard){

        String x = "XXXX-XXXX-XXXX-";
        //line 1
    }

    public static void main(String[] args) {
        System.out.println(maskCC("1234-5678-1234-2324"));
        System.out.println(maskCC("4567-5678-1234-5643"));
        System.out.println(maskCC("1234-5678-1234-4654"));
        System.out.println(maskCC("4567-5678-1234-5435"));
    }

}

可以插入"第1行和第34行的可能选项:

    A) 
    return x + creditCard.substring(15, 19);

    B) 
    StringBuilder sb = new StringBuilder(x);
    sb.append(creditCard, 15, 19);
    return sb.toString();

我认为这里最好的选择,因为A和B为我们提供了相同的输出,是B,因为它使用的是StringBuilder,这意味着它的方法是可变的,因此它将使用比选项A更少的内存。

我错了吗?可能是针对这种特殊情况的选项A是最好的选择吗?

2 个答案:

答案 0 :(得分:5)

选项a和b是相同的,因为Java编译器会将选项a 转换为选项b。您可以在方法之外移动x的声明(并使其成为final)。像,

static final String x = "XXXX-XXXX-XXXX-";
public static String maskCC(final String creditCard) {
    return x + creditCard.substring(15, 19);
}

使用javap检查第一个,第二个。 Java代码,

String x = "XXXX-XXXX-XXXX-";
String creditCard = "1234-5678-1234-23324";
String x2 = x + creditCard.substring(15, 19);
StringBuilder sb = new StringBuilder(x);
sb.append(creditCard, 15, 19);
String x3 = sb.toString();

生成类似的字节代码(注意 6-31 32-58 )< / p>

 0: ldc           #16                 // String XXXX-XXXX-XXXX-
 2: astore_1
 3: ldc           #18                 // String 1234-5678-1234-23324
 5: astore_2
 6: new           #20                 // class java/lang/StringBuilder
 9: dup
10: aload_1
11: invokestatic  #22                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
14: invokespecial #28                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
17: aload_2
18: bipush        15
20: bipush        19
22: invokevirtual #31                 // Method java/lang/String.substring:(II)Ljava/lang/String;
25: invokevirtual #35                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #39                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_3
32: new           #20                 // class java/lang/StringBuilder
35: dup
36: aload_1
37: invokespecial #28                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
40: astore        4
42: aload         4
44: aload_2
45: bipush        15
47: bipush        19
49: invokevirtual #43                 // Method java/lang/StringBuilder.append:(Ljava/lang/CharSequence;II)Ljava/lang/StringBuilder;
52: pop
53: aload         4
55: invokevirtual #39                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
58: astore        5
60: return

答案 1 :(得分:2)

变体A return x + creditCard.substring(15, 19);的一大优势在于它简单而干净,适用于从18的所有Java版本。在其编译的表单使用StringBuffer的情况下,对Java 5或更新版本的简单重新编译将使其使用StringBuilder。当您手动使用StringBufferStringBuilder时,这种灵活性会丢失。

确切的编译表格不是固定的。由于方法String.substring的语义不是由Java语言规范修复的,因此编译器通常不会触及它并将其编译为普通的方法调用。规范鼓励编译器供应商使用StringBuilder进行字符串连接(+运算符),只要有好处,大多数编译器都会这样做,即使没有任何好处。在此,xsubstring的结果均为String,因此简单String.concat会更简单,但大多数编译器始终使用StringBuilder,编译变体A相当于
return new StringBuilder().append(x).append(creditCard.substring(15, 19)).toString();

将这种典型形式与您的变体B进行比较,我们可以得出结论,变体B在性能方面具有两个优势:

  • new StringBuilder(x)StringBuilder初始化为x.length()+16的容量,该容量足以进行整个操作,而默认容量new StringBuilder()通常用于变体A,固定为16个字符,因为我们有19字符的结果而错过了这里的标记,因此将重新分配和复制基础字符数组

  • sb.append(creditCard, 15, 19);将复制四个字符,而无需为这些字符创建中间String表示。 substring操作的费用因实施而异,例如在Oracle的实现中,版本1.7.0_06发生了重大变化;从这个版本开始,子字符串需要一个新的char[]数组,其中包含受影响的字符数据的副本,因为它没有单独的offsetlength字段

但请注意,变体A和B的所有这些差异仅影响要执行的操作的形式描述。实际会发生什么,取决于JVM / JRE,通常Hotspot优化器知道许多与字符串相关的操作,并且可能融合操作或省略中间字符串表示。因此,有关绩效的结果相当不可预测,可能会受到subtle changes to the implementation的影响。

这就是为什么开发人员可能会坚持使用变体A,正如所说的那样,更简单,更易读,并且只有在分析器告诉他们存在可以通过处理Stringbuilder来解决性能问题时才关心性能。手动