什么是Java中的String Interning,何时应该使用它,为什么?
我有一个关于Java中的字符串的简单问题.以下简单代码段仅连接两个字符串,然后将它们与之进行比较==.
String str1="str";
String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
Run Code Online (Sandbox Code Playgroud)
比较表达式concat=="string"返回false那么明显(I明白之间的差值equals()和==).
当这两个字符串被声明时final,
final String str1="str";
final String str2="ing";
String concat=str1+str2;
System.out.println(concat=="string");
Run Code Online (Sandbox Code Playgroud)
比较表达式concat=="string",在这种情况下返回true.为什么会final有所作为?它是否必须与实习生池做某事或我只是被误导?
我看到很多这样的遗留代码:
class A {
public static final String CONSTANT = "value".intern();
...
}
Run Code Online (Sandbox Code Playgroud)
我没有看到intern()的任何原因,因为在Javadoc中可以读取:"所有文字字符串和字符串值常量表达式都被实现." 是否有一些这样的意图,也许是在过去的语言修订版中?
代码如下:
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
String s3 = new String("1")+new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
Run Code Online (Sandbox Code Playgroud)
上面代码的输出是:
false
true
Run Code Online (Sandbox Code Playgroud)
我知道s并且s2是不同的对象,因此结果的计算结果为false,但第二个结果的计算结果为true.有人能告诉我区别吗?
正如在本文中所解释的那样,我们应该在字符串常量上使用字符串的实际方法发布字符串文字是自动合并但是对于使用new构造的对象不是,因此使用该实习方法.但即使我们使用实习方法,也会创建一个新对象,那么实习方法的用途是什么?
String s = "Example";
String s1 = new String("Example"); // will create new object
String s2 = new String("Example").intern(); // this will create new object
// but as we are calling intern we will get reference of pooled string "Example"
Run Code Online (Sandbox Code Playgroud)
现在
System.out.println(s == s1); // will return false
System.out.println(s == s2); // will return true
System.out.println(s1 == s2); // will return false
Run Code Online (Sandbox Code Playgroud)
那么实习方法的用途是什么?
编辑
我理解实习生方法的工作,但我的问题是为什么有实习方法?因为要调用intern方法我们必须使用new创建字符串对象,这将创建新的字符串实例!
String s3 = new String("Example"); // again new …Run Code Online (Sandbox Code Playgroud) public static void main(String[] args) {
String s1 = new String("aa");
s1.intern();
String s2 = "aa";
System.out.println(s1 == s2);
//wrong in JDK1.6 but true in JDK1.8
String str1 = new String("str") + new String("01");
str1.intern();
String str2 = "str01";
System.out.println(str1 == str2);
}
Run Code Online (Sandbox Code Playgroud)
我用JDK1.8运行上面的代码,我认为结果会得到两个"falses",因为在我看来,很明显s1和str1位于堆中,而s2和str2是在字符串中实现的游泳池,但我得到了"假"和"真实".问题来了:是什么导致"真实"?
以上是原始问题.现在要确定这个问题远不是这些被称为重复的问题,我想谈谈我的新发现:代码的第二部分使用JDK1.6获得"错误"结果,而使用JDK1.8获得"真实"结果.一些博客称,在JDK1.7发布后,intern()的行为发生了变化.
如果池不包含与此String对象相等的字符串,则此String对象将不会添加到池中,而是将对此String对象的引用添加到池中.这意味着池中的引用将被分配给位于其他位置的字符串对象(如堆),并且下一次文本字符串的初始化等于早期的字符串对象也将被分配给字符串对象.这正好描述了代码part2关于"真实"结果.
这个理论确实可以用来解释上面代码的结果.但很明显,该理论不属于intern()doc所包含的内容,这在JDK6/8 API中几乎相同.
现在的问题是:对JDK1.6和JDK 1.8中相同代码的不同结果有没有更好的解释?我上面提到的理论究竟是真正发生的吗?
我知道有两种方法可以在Java中创建String:
String a = "aaa";
String b = new String("bbb");
Run Code Online (Sandbox Code Playgroud)
第一种方式,Java肯定会在字符串池中创建一个String对象并a引用它.(假设"aaa"之前不在游泳池里.)
使用第二种方法,将在堆中创建一个对象,但是jvm还会在字符串池中创建一个对象吗?
在这篇关于Java字符串池的问题中,@ Jesper说:
如果你这样做:
Run Code Online (Sandbox Code Playgroud)String s = new String("abc");然后在池中将有一个String对象,一个代表文字"abc",>的对象,并且将有一个单独的String对象,而不是在池中,它包含池对象的>内容的副本.
如果这是真的,那么每次使用new String("bbb");,在池中创建一个对象"bbb",这意味着通过上面的任何一种方式,java将始终在池中创建一个字符串对象.然后intern()用于什么?在文档http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#intern()中,它说:
调用实习方法时,如果池已经包含等于此字符串对象的字符串(由equals(Object)方法确定),则返回池中的字符串.否则,将此String对象添加到池中,并返回对此String对象的引用.
这意味着有些情况下池中没有字符串,这可能吗?哪一个是真的?
我正在尝试使用Java高效加载大型CSV格式的文件(通常为200-600mb)(内存更少,访问速度更快).目前,该程序正在使用字符串数组列表.之前使用Lua程序处理此操作,该程序使用每个CSV行的表和用于保存每个"行"表的表.
以下是内存差异和加载时间的示例:
如果我没记错的话,Lua表中的重复项存在作为对实际值的引用.我怀疑在Java示例中,List正在保存每个重复值的单独副本,这可能与更大的内存使用量有关.
以下是CSV文件中数据的一些背景知识:
以下是加载数据可能需要的一些示例:
我的问题 - 是否有一个集合需要更少的内存来保存数据但仍然提供了轻松快速地搜索/排序数据的功能?
public static void main (String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
s1.intern();
s2.intern();
System.out.println(s1 == s2); // why this returns false ?
}
Run Code Online (Sandbox Code Playgroud)
根据我的理解,第一次调用实习方法应该创建一个带有单个字符串的"字符串实习池" "hello".第二次调用实习方法没有做任何事情(因为"hello"字符串已经存在于池中).现在,当我说s1 == s2我希望JVM比较"hello"字符串实习池中的字符串并返回时true.
这是我的代码,我现在对此输出的字符串池和堆存储非常困惑.
public class String1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
String str = "abcd";
String str1 = "" ;
str1=str1+"abcd";
if(str.equals(str1))
System.out.println("True");
else
System.out.println("False");
if(str == str1)
System.out.println("True");
else
System.out.println("False");
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我正在创建String str并将存储在字符串池中(如果我出错了,请纠正我!).现在,在str1使用字符串连接后,"abcd"它们都具有相同的值.所以,我认为strstr1应该在String池中有相同的引用,所以,第二个if语句应该打印true但是打印false.
那么,我的问题为什么str和str1没有得到相同的参考?
假设我们有这样的程序:
import java.io.*;
public class ReadString {
public static void main (String[] args) {
// prompt the user to enter their name
System.out.print("Enter your name: ");
// open up standard input
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String userName = null;
String userNameCopy = null;
// read the username from the command-line; need to use try/catch with the
// readLine() method
try {
userName = br.readLine();
System.out.print("Enter your name once again: ");
userNameCopy = br.readLine();
} catch (IOException ioe) { …Run Code Online (Sandbox Code Playgroud) 如果我写:
String s = new String("abc");
Run Code Online (Sandbox Code Playgroud)
它创建一个类型String为"abc"的对象.如果我写:
String s = "abc";
Run Code Online (Sandbox Code Playgroud)
这也会创建一个值为"abc"的对象.如何在不遇到新关键字的情况下创建对象.
如果我写:
s.append("def");
Run Code Online (Sandbox Code Playgroud)
它创建了另外两个字符串对象:
a. "abcdef"
b. "def"
Run Code Online (Sandbox Code Playgroud)
因此,在遇到双重引号内的任何内容时,都会创建一个新String对象.这是怎么发生的?
我听说你执行时会创建两个对象String s = new String("lol");.为字符串常量池创建一个对象s,在堆上创建一个对象.
那么,当我们执行以下操作时是否创建了2个对象?String s = "lol";对象创建是否相同?
编辑:
创建了多少个对象:
String s1 = new String("lol1");
以及多少:
String s2 = "lol2";