【问题标题】:What is happening when you "alter" a string in Java using "+="?在 Java 中使用“+=”“更改”字符串时会发生什么?
【发布时间】:2016-11-11 20:37:51
【问题描述】:

我了解 Java 中的 String 变量是不可变的,因此无法更改。

String myString = "Hello.";
myString += " ";
myString += "My name is Kevin"; 

每次我们向这个String()“添加”一些东西时,我们实际上是在创建一个新的String(),但它与它所连接的字符串具有相同的名称。这是否意味着内存中有多个引用变量“myString”?

【问题讨论】:

  • 您误解了引用与其引用的实际对象之间的区别。连接时,引用只是指向新创建的字符串对象。
  • 你所谓的name其实就是一个variable。对象和引用没有名称。一个变量可以在不同的时间引用不同的对象,不同的变量可以引用同一个对象——这就是所谓的aliasing。

标签: java string performance memory concatenation


【解决方案1】:

每次您“修改”/连接 String 和 += 时,都会创建一个新的 String 对象并将名为 myString 的引用替换为新创建的 String。所以不,内存中只有一个引用,但是引用指向的对象每次都在变化。该字符串是不可变的,因此无法“就地”修改对象。

String 是 Java 中的不可变类。不可变类只是其实例无法修改的类。实例中的所有信息都是在创建实例时初始化的,不能修改信息。不可变类有很多优点。

great answer on the Programmer's StackExchange 解释了为什么Strings 在 Java 中是不可变的,以及有关其工作原理的更多详细信息。

最好的方法是使用StringBuilder() 类:

String myStringBuilder = new StringBuilder("Hello.");
myStringBuilder.append(" ");
myStringBuilder.append("My name is Kevin");

System.out.println(myStringBuilder.toString());

但是,现代 Java 编译器会自动将 String 连接转换为 StringBuilder 操作。

【讨论】:

  • 如果内存中只有一个引用,那么与使用StringBuilder() 相比,为什么这种String 修改被认为效率低下?当然,如果只有一个引用,那么在使用 += 修改 String 时内存使用不会改变?
  • 因为使用+=需要每次都创建新的String()对象,而StringBuilder()是可变的,可以就地修改。
  • 我的程序中实际上已经有了确切的代码,我了解 String builder 的工作原理 我只是想弄清楚如果内存中只有一个引用,原始方法的效率会如何降低。因此创建了一个新引用,但变量的旧引用留给垃圾收集器(检查 Mehdi 的答案)?
  • 知道了;也许编辑会帮助一些以前没有使用过StringBuilder 的人。对,旧的String 对象将很快被垃圾回收,因为不再有对旧对象的任何引用。
  • 谢谢。我现在完全明白了,我是 Java 新手,以前在编程中从未遇到过“垃圾收集”。
【解决方案2】:

不,您无法访问以前的参考,它留给垃圾收集器来收集它。换句话说,内存中只有一个引用保存了变量的当前值("My Name is Kevin)

请注意,如果您要大量更改 String 变量,则应使用 StringBuilder 类。

这里是 StringBuilder 类文档的链接 你也可以在网上找到很多使用这个类的例子

https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html

这里也是你问题的详细答案

When will a string be garbage collected in java

【讨论】:

  • 所以之前版本的myString 变量仍然存在于内存中,但是因为它们不再被引用,所以它们将留给“垃圾收集器”?
  • 是的,正如您在我添加回答的链接中看到的那样,字符串中垃圾收集的规则与其他规则相同。
【解决方案3】:
String myString = "Hello.";

上面创建了一个新的Object并将"Hello."放入内存中,myString引用了Object。

myString += " ";

在这里您可以看到 myString = myString + " "; 一个 " " 字符串已创建并放入内存中,连接将导致创建一个新的 String,该 myString 引用。

请注意,字符串与"+" 的连接复杂度为 O(N2),而StringBuilder 的复杂度为 O(N)。这是因为每次都会为连接结果创建新的String,而StringBuilder 则包含一系列字符。因此String的连接效率不如StringBuilder,尤其是需要构建大量的String。

【讨论】:

    【解决方案4】:

    Java 使用字符串池。这是string interning概念的实现。

    在计算机科学中,字符串实习是一种仅存储每个不同字符串值的副本的方法,该副本必须是不可变的。驻留字符串使某些字符串处理任务更节省时间或空间,但代价是在创建或驻留字符串时需要更多时间。不同的值存储在字符串实习池中。

    这意味着对于每个字符串,该特定字符串的副本都会添加到字符串池中。每个包含该确切字符串的变量都指向字符串池中的该副本。


    字符串Hello.、 和My name is Kevin 被添加到字符串池中,因为它们是程序中的文字。

    String myString = "Hello.";
    

    变量myString开始指向字符串Hello.,该字符串已经在字符串池中。

    myString += " ";
    

    字符串Hello.(注意末尾的多余空格)被添加到池中。变量 myString 现在指向它。

    myString += "My name is Kevin";
    

    字符串Hello. My name is Kevin 被添加到池中。变量 myString 现在指向那个。
    池中不再被变量引用的字符串are eligible for garbage collection,因此Hello.(末尾有空格)现在可以被垃圾回收。

    【讨论】:

    • 所以“可能被清除”是指内存中的地址被旧版本的myString 使用,但会在编译时被清除?
    【解决方案5】:

    在此处定义 String 引用变量 myString 的代码。

    String是一个不可变类,表示对象一旦创建就不能再修改了。

    这里"Hello" 创建一个字符串对象并通过myString 引用该对象。

    String myString = "Hello.";  //create a Object String contain Hello and refer to myString
    myString += " ";
    myString += "My name is Kevin";
    

    当myString+=" "; 被执行时,它通过在其中连接“Hello”来创建另一个新的String对象。

    "Hello " 创建了一个新对象,myString 引用它。 "Hello" 符合垃圾回收条件。

    再次当myString += "My name is Kevin"; 被执行时,它通过在其中连接"Hello " 创建另一个新的字符串对象。 您的新对象 "Hello My Name is Kevin" 由字符串类型的 myString 引用变量引用。

    现在,您的早期对象 "Hello" 和 "Hello/s" 没有被任何其他引用变量引用,因此,它有资格进行垃圾回收。

    谢谢

    【讨论】:

    • 干杯,垃圾收集过程现在完全有意义了。
    猜你喜欢
    • 1970-01-01
    • 2021-05-23
    • 1970-01-01
    • 2022-06-17
    • 2018-10-23
    • 2018-10-04
    • 2023-04-08
    • 1970-01-01
    • 2010-11-14
    相关资源
    最近更新 更多