为什么Java中没有String.Empty?


260

我知道每次键入字符串文字时"",字符串池中都会引用相同的String对象。

但是,为什么String API不包含public static final String Empty = "";,所以我可以使用引用String.Empty

至少,这将节省编译时间,因为编译器将知道引用现有的String,而不必检查是否已创建它以进行重用,对吗?我个人认为,字符串文字(尤其是很小的文字)的泛滥在许多情况下是“代码异味”。

那么是否没有String.Empty背后有一个宏伟的设计原因,还是语言创建者根本就没有分享我的观点?


5
Aidanc:我想他的意思是,你做的东西一样的情况下outputBlah = "",他可能喜欢something == String.Emptysomething.Length > 0,以及(你跳过空校验)
Skurmedel

2
@Aidanc-他正在寻找诸如Collections.EMPTY_SET之类的“空成员” ,而不是检查字符串“空”的函数。
Tim Stone,2010年

2
@Aidanc:真正激发灵感的是'TextBox.setText(“”);'。
汤姆·特雷桑斯基

3
有一个String.isEmpty()功能...你为什么要String.EMPTY
Buhake Sindi

8
String.isEmpty()不返回空字符串。
郭富城2010年

Answers:


191

String.EMPTY是12个字符,并且""是两个,并且它们在运行时都将引用内存中的完全相同的实例。我不完全确定为什么String.EMPTY要节省编译时间,实际上我认为是后者。

特别是考虑到Strings是不可变的,并不是像您首先可以获取一个空的String并对其执行一些操作-最好使用a StringBuilder(或者StringBuffer如果您希望是线程安全的)将其转换为String。

更新
从您的评论到问题:

真正的启发是 TextBox.setText("");

我相信在您的适当课程中提供一个常量是完全合法的:

private static final String EMPTY_STRING = "";

然后在您的代码中引用它

TextBox.setText(EMPTY_STRING);

这样,至少您可以清楚地知道您想要一个空String,而不是忘记在IDE或类似内容中填写String。


14
我仍然会为您+1,但是我感到很脏,因为您提到的原因是StringBuilder没有谈论十分之九的情况,StringBuilder而不是将其串联起来是完全不合适的。
Randolpho

84
我倾向于使用string.empty,主要是因为它更明确。另外,在某些速率情况下,可能很难在视觉上区分“”和“'”之类的东西。最后,正如其他人所指出的那样,当我们对真正的工作感到无聊时,这只是那些无意义的事情中的一种使我们感到困惑。=)
JohnFx 2010年

@Nodel M:关于编译时间,我假设如果在2个不同的源文件中定义了2个具有相同字符串值的字符串文字,那么当编译器到达第2个时,它需要进行某种检查以找出“嘿,我已经从这里开始了解这个字符串了。” 我当然不是Java编译器的专家,但是怎么可能不这样呢?而且我认为跳过该检查将导致编译时间的微小改进。
汤姆·特雷桑斯基

@Tom-我相信String实习是在运行时完成的,而不是在编译时完成的,因此实际上,如果您将空字符串作为另一个文件中的常量,则编译器需要引用该类以解析为String文字。
Noel M 2010年

1
@Randolpho使用字符串连接时,您实际上是在后台使用StringBuilder。
whiskeysierra

133

org.apache.commons.lang.StringUtils.EMPTY


29
看起来比空的“”好得多,也更容易阅读。我希望不仅仅是我。
Lakatos Gyula

2
@LakatosGyula-我想可能是(只是你)。精通Java的程序员在阅读""...时没有问题,大多数人可能会大声反对使用,EMPTY除非在特定情况下EMPTY具有特定领域的含义。(在这种情况下,可能会有一个更合适的名称。)
Stephen C

14
@LakatosGyula不只是你。我从Java到.NET开发,String.Empty是我很高兴在框架中找到的功能。我喜欢它在空的引号上的明确性质。
yohohoho 2015年

63
@StephenC当我看到一个空的“”时,我首先想到的是它是一个错误,有人没有完成函数等。使用String.EMPTY,我确切地知道开发人员打算返回一个空字符串。
Lakatos Gyula

1
在所有情况下,当linter说“使用命名常量而不是blah blah blah”时,这也很有帮助。每个程序员都知道“”不是魔术,但不必向客户解释会更好。
LizH

28

如果要与空字符串进行比较而不必担心null值,则可以执行以下操作。

if ("".equals(text))

最终,您应该做自己认为最清楚的事情。大多数程序员认为“”表示空字符串,而不是有人忘记在其中输入任何内容的字符串。

如果您认为有性能优势,则应该对其进行测试。如果您不认为它值得您自己测试,那么它很好地表明它确实不值得。

听起来您想尝试解决15年前设计该语言时就解决的问题。


1
我参加聚会很晚了,但是,由于Java字符串是不可变的,因此我认为JVM中的所有空字符串只是对同一String对象的不同引用。因此,以下内容也很正确: if ("" == text)
Ajoy Bhatia'4

12
@AjoyBhatia问题是您可以创建新的空字符串。if ("" == new String())是假的。更好的测试if(text.isEmpty())
彼得·劳瑞

1
@AjoyBhatia-仅在字符串已插入的情况下。stackoverflow.com/questions/10578984/what-is-string-interning
Davor 2014年

9

如果确实需要String.EMPTY常量,则可以在项目中创建一个名为“ Constants”的实用程序静态最终类(例如)。此类将维护您的常量,包括空的String ...

按照同样的想法,您可以创建ZERO,一个int常量...在Integer类中不存在,但是就像我评论的那样,编写和阅读它们很麻烦:

for(int i=Constants.ZERO; ...) {
    if(myArray.length > Constants.ONE) {
        System.out.println("More than one element");
    }
}

等等。


8

Apache StringUtils也解决了这个问题。

其他选项失败:

  • isEmpty()-不是null安全的。如果字符串为空,则抛出NPE
  • length()== 0-再次不是null安全的。也没有考虑空格字符串。
  • 与EMPTY常数的比较-可能不是null安全的。空格问题

Granted StringUtils是另一个可以拖拽的库,但是它工作得很好,节省了时间,并且麻烦检查空值或妥善处理NPE。


3
所以......这似乎是唯一安全的选择是可怕的尤达条件:"".equals(s)
Lie Ryan

8

不要只说“字符串存储池以原义形式重用,大小写关闭”。编译器在幕后所做的不是重点。这个问题是合理的,特别是考虑到收到的赞成票数量。

这是关于对称性的,没有对称性,API很难用于人类。众所周知,早期的Java SDK忽略了该规则,现在为时已晚。这是我脑海中的几个例子,您可以随意使用“最喜欢的”例子:

  • BigDecimal.ZERO,但没有AbstractCollection.EMPTY,String.EMPTY
  • Array.length但List.size()
  • List.add(),Set.add(),但Map.put(),ByteBuffer.put()和我们不要忘记StringBuilder.append(),Stack.push()

即使您将List参数命名为length(),您仍然需要括号,因为它是一种方法。Array.length是公共的最终变量,仅因为Array是不可变的,所以它才有效。因此,您仍然拥有Array.length和List.length()。我认为这更令人困惑,更容易出错。至于.append()和.push(),尽管它们确实执行类似的任务,但我认为它们已适当命名。追加字符串正是您要执行的操作,但是您没有“追加”堆栈,而是推送和弹出值。而且StringBuilder.push()暗示StringBuilder.pop(),这是不可能的。
克雷格·帕顿

来自模板/泛型的一致接口也有助于算法。如果算法需要一个集合的长度,那么我们只需要length(T)或T.length()即可。类似地,可以通过通用add()或append()将堆栈,列表或字符串添加到堆栈的末尾。您提到Java中的数组是具有暴露的length属性的不可变/内置类型。很好,这并不意味着编译器无法处理或生成length(T)或T.length()的代码。Kotlin针对各种情况生成了许多内在方法。
Slawomir,

但是一个统一命名的length()方法只能让我们检查长度。那有多有用?如果您的目标是抽象列表和数组以使其可以通过接口以某种方式使用,则还需要一种一致的方式来读取或写入数据。因此,现在您需要生成get(),set()和add()方法。从本质上讲,您是在创建功能较弱的Array的List视图。由于Arrays.asList()可用,易于使用且轻巧,为什么要重新发明轮子呢?数组,列表,StringBuilders和堆栈都有特定的用途。似乎最好将您的界面设计为使用最合适的界面。
克雷格·帕顿

5

所有这些""文字都是同一个对象。为什么要增加所有这些额外的复杂性?它的键入时间更长而不清楚(编译器的成本很小)。由于Java的字符串是不可变的对象,因此根本不需要区分它们,除非可能是为了提高效率,但是使用空字符串文字并不重要。

如果您真的想要一个EmptyString常数,请自己制作。但是它所要做的就是鼓励更多冗长的代码。有将永远是任何利益这样做。


27
x = String.Empty传达意图比传达更好x = ""。后者可能是偶然的遗漏。说永远没有好处是不正确的。
Jeffrey L Whitledge

@Jeffrey:我不认为我特别同意。我想,这是其中没有一成不变的规则的事情之一。
Donal Fellows 2010年

是的,必须指出,java编译器会在字符串池中创建新实例之前检查字符串文字是否已经存在。
rds 2013年

1
@Jeffrey-知道这是一个非常古老且主观的讨论。x = String.Empty传达意图,真实。但是假设该语言提供了一个常量String.Empty,那么当您遇到语言时,x = ""您仍然对意图的了解就好像没有常量一样。您需要确保世界Java代码中没有使用空字符串的所有地方,""以获取您提到的信息。具有讽刺意味的是,C#确实使用了常量并鼓励使用它,因此正如我所说,我知道这是一个非常认真的讨论。
chiccodoro 2014年

@chiccodoro-是的,是的。这就是为什么空字符串文字""应该是非法的,以排除意外。我在开玩笑!
杰弗里·惠特里奇


3

我知道每次我键入String文字“”时,在String池中都会引用相同的String对象。
没有这样的保证。而且您不能在应用程序中依赖它,这完全取决于jvm的决定。

还是语言创建者根本不同意我的观点?
是的 对我来说,这似乎是优先级较低的事情。


6
没有这样的保证 ...好吧,JLS 确实指出了这种情况。
Tim Stone,2010年

@Tim除非您拨打“实习生”电话,否则不会。以编程方式构造两个相等的大字符串并进行检查很容易。
Nikita Rybak 2010年

@Tim例如,重复一个+ =“ a”; 100次,对b进行相同的操作并检查。
Nikita Rybak 2010年

5
没错,但是您所描述的不是字符串文字,也不是在编译时可以保证结果的表达式(例如String username = "Bob" + " " + "Smith";)。以编程方式创建的字符串不能保证被实习,除非您intern()按照声明明确调用。OP的场景描述了""在整个代码中使用空白字符串字面量,在这种情况下会发生自动插入。
蒂姆·斯通·史东

@Tim String a = ""; for(int i = 0; i < 100; i++) {a += "a";} String b = ""; for(int i = 0; i < 100; i++) {b += "b";} a.intern(); b.intern();现在ab指向PermGen中的相同内存位置。看到这篇文章
1ac0 2014年

1

答案较晚,但我认为它为该主题添加了新内容。

先前的答案均未回答原始问题。有些人试图证明缺乏常数是合理的,而另一些人则展示了我们可以解决常数缺乏的方法。但是,没有人为常量的利益提供令人信服的理由,因此仍然缺乏适当的解释。

常量将很有用,因为它可以防止某些代码错误不被注意。

假设您有一个庞大的代码库,其中包含对“”的数百个引用。有人在滚动代码时修改其中之一,并将其​​更改为“”。这样的更改很有可能会被忽视而无法投入生产,这时可能会导致某些问题,其来源很难被发现。

OTOH,一个名为EMPTY的库常量,如果遭受相同的错误,将为诸如EM PTY之类的内容生成编译器错误。

定义自己的常量仍然更好。仍有人可能会错误地更改其初始化,但是由于其用途广泛,因此与单个用例中的错误相比,更难以发现这种错误的影响。

这是使用常量而不是文字值所获得的一般好处之一。人们通常认识到,将常量用于数十个位置中的值可以使您仅在一个位置中轻松更新该值。鲜为人知的是,这也防止了该值被意外修改,因为这种更改会在所有地方显示。因此,是的,“”比“ EMPTY”短,但使用“ EMPTY”比“”更安全。

因此,回到最初的问题,我们只能推测语言设计人员可能没有意识到为经常使用的文字值提供常量的好处。希望我们有一天会看到在Java中添加了字符串常量。


-16

对于那些声称""String.Empty可以互换,或者""是更好的,你是非常错误的。

每次执行诸如myVariable =“”的操作时;您正在创建对象的实例。如果Java的String对象具有EMPTY公共常量,则对象“”只有1个实例

例如:-

String.EMPTY = ""; //Simply demonstrating. I realize this is invalid syntax

myVar0 = String.EMPTY;
myVar1 = String.EMPTY;
myVar2 = String.EMPTY;
myVar3 = String.EMPTY;
myVar4 = String.EMPTY;
myVar5 = String.EMPTY;
myVar6 = String.EMPTY;
myVar7 = String.EMPTY;
myVar8 = String.EMPTY;
myVar9 = String.EMPTY;

10(11个字符串,包括String.EMPTY)指向1个对象的指针

要么: -

myVar0 = "";
myVar1 = "";
myVar2 = "";
myVar3 = "";
myVar4 = "";
myVar5 = "";
myVar6 = "";
myVar7 = "";
myVar8 = "";
myVar9 = "";

10个指向10个对象的指针

这效率低下,并且在整个大型应用程序中可能很重要。

也许Java编译器或运行时足够有效,可以自动将“”的所有实例指向同一个实例,但可能不那么有效,并且需要进行额外的处理才能确定。


9
错误的,根据stackoverflow.com/questions/1881922/…,“”字符串将从字符串池中重用。
RealHowTo 2012年

1
我说过它可能会重用同一对象,如果这样,它的效率仍然较低,因为它需要在字符串池中找到该对象,所以我怎么错了?无论如何,String.Empty之所以具有优越性有几个原因,包括防止诸如myVar =“”之类的错误;我已经说过,可读性以及性能改进。如果没有其他原因,最好使用常量而不是创建字符串文字。维护代码更容易。
安东尼·布斯

1
我怀疑您的性能参数是否有效,因为JLS表示在编译时常量将被视为立即数(docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10。 5)。可读性更好。
RealHowTo 2012年

3
@AntonySmith-我想您需要再学习一些Java知识,或者您可能现在已经知道自己的错误了。Java字符串是不可变的,位于池中。因此,无论在代码中找到多少次,JVM中只有一个String对象用于“”。您可以通过执行以下操作来检查字符串是否为空if (text == "")
Ajoy Bhatia'4

2
错误。此评论应删除。
Elad Tabak
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.