我对Java中的StringPool感到困惑。我在阅读Java中的String章节时遇到了这个问题。用通俗的话来说,请帮助我理解StringPool实际做了什么。什么是Java中的字符串池?
回答
这将打印true
(尽管我们不使用equals
方法:正确的方式来比较字符串)
String s = "a" + "bc";
String t = "ab" + "c";
System.out.println(s == t);
当编译器优化您的字符串常量,它认为这两个s
和t
具有相同的价值,从而你只需要一个字符串对象。这是安全的,因为String
在Java中是不可变的。
因此,s
和t
指向相同的对象并保存一些小内存。
名称的'字符串池'来自这样的想法,即所有已定义的字符串都存储在某个“池”中,并且在创建新对象之前,对象编译器检查是否已经定义了这样的字符串。
我不认为它确实有很大的作用,它看起来就像是字符串文字的缓存。如果您有多个字符串的值相同,则它们都将指向字符串池中的相同字符串字面值。
String s1 = "Arul"; //case 1
String s2 = "Arul"; //case 2
在情况1中,文字s1被新创建并保存在池中。但在情况2中,文字s2引用s1,它不会创建新的。
if(s1 == s2) System.out.println("equal"); //Prints equal.
String n1 = new String("Arul");
String n2 = new String("Arul");
if(n1 == n2) System.out.println("equal"); //No output.
当JVM装载类,或以其他方式看到文字字符串,或一些代码intern
SA字符串,它添加字符串到具有每个这样的一个拷贝大多隐藏查找表串。如果添加了另一个副本,则运行时会对其进行排列,以便所有文字都引用相同的字符串对象。这被称为“实习”。如果你这样说
String s = "test";
return (s == "test");
它会返回true
,因为第一和第二个“测试”实际上是同一个对象。这样比较interned字符串可能比String.equals
更快,因为只有一个参考比较,而不是一堆char
比较,所以多得多比String.equals
更快。
您可以通过调用String.intern()
将字符串添加到池中,该字符串将返回字符串的合并版本(可能与您正在实习的字符串相同,但您会为此疯狂地依赖该字符串 - - 你经常无法确定哪些代码已经被加载并直到现在运行并实现相同的字符串)。合并版本(从intern
返回的字符串)将等于任何相同的文字。例如:
String s1 = "test";
String s2 = new String("test"); // "new String" guarantees a different object
System.out.println(s1 == s2); // should print "false"
s2 = s2.intern();
System.out.println(s1 == s2); // should print "true"
我其实并不认为这是在运行时完成的。即使使用方法构造的最简单的字符串也不会被合并。例如。,如果我使用_concat_而不是_con __ _ – 2010-09-27 06:41:07
@Nikita:那么我的答案中的示例将不起作用,这是因为'concat'不能被轻松优化。用'+'拼凑在一起的字符串可能会被任何自尊的编译器预先收集,因为值永远不会改变。但是编译器无法真正猜测一个函数是否会一直返回相同的值(有些不会),所以它不会尝试。如果在你的例子中使用'concat'而不是“ab”,“c”,“a”和“bc”,但“abc”不会(因为它不是一个文字, t' intern' it)。然而,用'+'编译器会看到这两个字符串都是“abc”并编译它。 – cHao 2010-09-27 08:25:42
实习将*在*运行时完成,因为(1)池总是空出来的,(2)两个不同的类每个都可以有“abc”。如果interning是编译时的事情,并且两个类最终都被加载,那么最终会在字符串池中出现两个“abc”,这会破坏字符串池的整个目的。 – cHao 2010-09-27 08:45:28
让我们开始从虚拟机规格报价:
加载包含文字可能会创建一个新的String对象的String类或接口(§2.4.8)来表示该文字。如果已经创建了一个String对象来表示该文本的先前出现,或者如果String.intern方法已经在表示与文字相同的字符串的String对象上被调用,则可能不会发生这种情况。
这可能不会发生 - 这是一个暗示,那有什么特殊String
对象。通常,调用一个构造函数将会总是创建一个新的类实例。对于字符串来说情况并非如此,尤其是当字符串对象用文字“创建”时。那些字符串存储在全局存储(池)中 - 或者至少引用保存在一个池中,并且每当需要已知字符串的新实例时,vm都会从池中返回对该对象的引用。在伪代码,它可能会这样:
1: a := "one"
--> if(pool[hash("one")] == null) // true
pool[hash("one") --> "one"]
return pool[hash("one")]
2: b := "one"
--> if(pool[hash("one")] == null) // false, "one" already in pool
pool[hash("one") --> "one"]
return pool[hash("one")]
在这种情况下
所以,变量a
和相同对象b
保持引用。在这种情况下,我们有(a == b) && (a.equals(b)) == true
。
这是不是这样,如果我们使用的构造函数:
1: a := "one"
2: b := new String("one")
再次,"one"
在该池上创建,但随后我们创建从相同的字面一个新的实例,在这种情况下,它会导致(a == b) && (a.equals(b)) == false
所以为什么我们有一个字符串池吗?字符串和特别是字符串文字被广泛用于典型的Java代码中。它们是不变的。不可变的被允许缓存String以节省内存并提高性能(减少创建工作量,减少垃圾收集)。
正如我们不必在意字符串池中,只要我们牢记程序员:
(a == b) && (a.equals(b))
可能true
或false
(总是使用equals
比较字符串)- 不要使用反射来改变字符串的支持
char[]
(因为你不知道是谁在使用该字符串actualling)
如果您关心字符串池,那么在广泛使用一小组字符的应用程序中,可能会大幅提升性能,通常用作令牌或关键字。一旦字符串被执行,比较变成一个'==',而不是函数调用,两个length()调用,以及'equals'可能发生的一堆字符比较。 – cHao 2010-09-27 09:09:51
@cHao为了安全性和一致性,您仍然可以对字符串使用'String.equals()',因为'String.equals()'首先执行'=='比较 – bcoughlan 2014-10-01 11:00:14
@bcoughlan:'=='是安全的,一致为“平等” - 这只是误解。与物体一起使用的人分为两类。有些人不明白价值vs身份语义(并且==与引用类型比较身份) - 那些人*应该*总是使用'String.equals'。然后有些人明白,但有意识地选择身份。只要你知道你的物体来自哪里,那就可以可靠地工作。有一个原因,“==”与对象一起工作 - 特别是为什么它不只是调用equals。 – cHao 2014-10-01 14:02:00
- 1. 为什么字符串存储在Java中的字符串池?
- 2. Java的字符串文字池
- 3. 是什么堆内存和字符串常量池之间的Java
- 4. 什么是Java字符串compareTo方法和什么是Java字典序
- 5. 关于Java的字符串池
- 6. 为什么使用new运算符创建的字符串在字符串池中创建字符串literal
- 7. CSS中的`〜`代字符串是什么?
- 8. C#中字符串前的@是什么?
- 9. 什么是Perl中的“字符串化”?
- 10. 什么是.NET中的字符串?
- 11. {}中的字符串是什么意思?
- 12. 什么是空字符串?
- 13. 什么是Uri字符串?
- 14. 在C#中的区别是什么:字符串VS字符串
- 15. 什么是Java 8中的字符串键的替代散列?
- 16. 为什么java中的字符串是不可变的?
- 17. 什么是Java中的字符串本地内存?
- 18. Java字符串中的底层容器是什么?
- 19. Java字符串池对象创建
- 20. 关于Java字符串常量池
- 21. Java常量池的用途是什么?
- 22. 如果字符串池的大小超过了什么?在字符串常量池的
- 23. 字符串池8
- 24. 我可以将字符串文字从字符串池移到java堆吗?为什么?
- 25. 什么DS用于搜索Java中的一组字符串中的字符串?
- 26. 为什么Kotlin的字符串插值在Java中串联字符串?
- 27. 字符串池(字符串和StringBuffer)
- 28. 字符串... <varname>什么是字符串...意思是
- 29. 什么把一个单个字符到Java中的字符串中的惯用方法是什么?
- 30. Understading字符串文字池
的Java有原始类型的包装类型,这些类也是不可变的。像Integer,Charecter和Double ....等。他们是否也有一个游泳池来节省内存?如果没有,String有什么特别之处? – 2015-02-02 09:21:05
@PunithRaj我并不确定!然而,我怀疑它。例如,int只有4个字节,所以最终不会因为两个Integer指向内存中的相同位置而节省太多。相反,不得不维护一个“整数池”来发现重复值,可能会浪费更多的内存,而不是通过避免重复值来节省。 – 2015-02-03 10:49:45
@PunithRaj字符串不是原始数据类型(技术上/执行方面明智)和字符串没有像char/int如何做的包装类。 – user3240361 2015-02-17 10:49:38