如何比较 Java 中的字符串?
Posted
技术标签:
【中文标题】如何比较 Java 中的字符串?【英文标题】:How do I compare strings in Java? 【发布时间】:2017-05-04 11:06:31 【问题描述】:到目前为止,我一直在我的程序中使用==
运算符来比较我的所有字符串。
但是,我遇到了一个错误,将其中一个改为.equals()
,它修复了这个错误。
==
不好吗?什么时候应该使用,不应该使用?有什么区别?
【问题讨论】:
另外很高兴知道,如果你覆盖了 .equals () 方法,请确保你覆盖了 .hashcode () 方法,否则你最终会违反等价关系 b/w equals 和哈希码。有关更多信息,请参阅 java 文档。 留下一个链接来解释为什么==
在对象上的工作方式:***.com/a/19966154/2284641
==
有时会起作用,因为 java 有一个字符串池,它会尝试重用常用字符串的内存引用。但是==
比较的是对象相等,而不是值...所以.equals()
是您想要使用的正确用途。
永远不要使用 == 来测试字符串是否相同,除非您喜欢跟踪细微的错误并研究 Java 字符串实习过程的复杂性。 "12"=="1"+2
是假的(可能)
【参考方案1】:
==
测试引用相等性(它们是否是同一个对象)。
.equals()
测试值是否相等(它们在逻辑上是否“相等”)。
Objects.equals() 在调用.equals()
之前检查null
,因此您不必这样做(从JDK7 开始可用,在Guava 中也可用)。
因此,如果您想测试两个字符串是否具有相同的值,您可能需要使用Objects.equals()
。
// These two have the same value
new String("test").equals("test") // --> true
// ... but they are not the same object
new String("test") == "test" // --> false
// ... neither are these
new String("test") == new String("test") // --> false
// ... but these are because literals are interned by
// the compiler and thus refer to the same object
"test" == "test" // --> true
// ... string literals are concatenated by the compiler
// and the results are interned.
"test" == "te" + "st" // --> true
// ... but you should really just call Objects.equals()
Objects.equals("test", new String("test")) // --> true
Objects.equals(null, "test") // --> false
Objects.equals(null, null) // --> true
您几乎总是想使用Objects.equals()
。在您知道您正在处理interned 字符串的罕见情况下,您可以使用==
。
来自JLS 3.10.5. String Literals:
此外,字符串文字总是指代
String
类的相同 实例。这是因为字符串字面量 - 或者更一般地说,作为常量表达式 (§15.28) 值的字符串 - 是“内部的”,以便使用String.intern
方法共享唯一实例。
类似的例子也可以在JLS 3.10.5-1找到。
其他需要考虑的方法
String.equalsIgnoreCase() 忽略大小写的相等值。但是请注意,此方法在各种与语言环境相关的情况下可能会产生意外结果,请参阅this question。
String.contentEquals() 将String
的内容与任何CharSequence
的内容进行比较(Java 1.5 起可用)。使您不必在进行相等比较之前将您的 StringBuffer 等转换为 String,而是将 null 检查留给您。
【讨论】:
如果 == 检查引用相等性,为什么 n==5 有意义? 5 不是变量 @HritRoy 因为==
检查变量的值。当您有一个对象时,引用该对象的变量将该对象的引用作为 value。因此,在将两个变量与==
进行比较时,您可以比较references。在比较诸如int
之类的原始数据类型时,情况仍然相同。 int
类型的变量将整数作为值。因此,您可以使用==
比较两个int
s 的值。如果int
是变量的值或幻数无关紧要。 另外:reference只不过是一个引用内存的数字。
我要补充一点,即使您知道您的字符串已被保留,您也应该使用equals
,因为它更明显是正确的。或者,您应该使用枚举而不是字符串。
鉴于类名是复数 (Objects
) 并且名称取自英文,我发现他们为方法保留名称 .equals
而不是将其更改为 @987654352 令人不快@.【参考方案2】:
是的,==
不适合比较字符串(实际上是任何对象,除非您知道它们是规范的)。 ==
只是比较对象引用。 .equals()
测试是否相等。对于字符串,它们通常是相同的,但正如您所发现的,这并不总是得到保证。
【讨论】:
【参考方案3】:==
比较 Java 中的对象引用,String
对象也不例外。
比较对象的实际内容(包括String
),必须使用equals
方法。
如果使用==
比较两个String
对象,结果是true
,那是因为String
对象被实习,并且Java 虚拟机有多个引用指向同一个实例String
。不应期望使用==
比较一个包含相同内容的String
对象与另一个String
对象以评估为true
。
【讨论】:
【参考方案4】:是的,这很糟糕......
==
表示您的两个字符串引用是完全相同的对象。您可能听说过这种情况,因为 Java 保留了某种文字表(它确实如此),但情况并非总是如此。一些字符串以不同的方式加载,由其他字符串构造等等,所以你绝不能假设两个相同的字符串存储在同一个位置。
Equals 为您进行真正的比较。
【讨论】:
【参考方案5】:.equals()
比较一个类中的数据(假设实现了函数)。
==
比较指针位置(对象在内存中的位置)。
==
如果两个对象(NOT TALKING ABOUT PRIMITIVES)都指向同一个对象实例,则返回 true。
.equals()
如果两个对象包含相同的数据,则返回 true equals()
Versus ==
in Java
这可能对你有帮助。
【讨论】:
【参考方案6】:==
运算符检查两个字符串是否完全相同。
.equals()
方法将检查两个字符串是否具有相同的值。
【讨论】:
一般我强烈推荐apache commons库:commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/…, java.lang.String)【参考方案7】:String a = new String("foo");
String b = new String("foo");
System.out.println(a == b); // prints false
System.out.println(a.equals(b)); // prints true
确保您了解原因。这是因为==
比较只比较引用; equals()
方法对内容进行逐个字符的比较。
当您为a
和b
调用new 时,每个人都会获得一个指向字符串表中"foo"
的新引用。引用不同,但内容相同。
【讨论】:
【参考方案8】:==
测试对象引用,.equals()
测试字符串值。
有时==
看起来像是在比较值,因为 Java 做了一些幕后工作以确保相同的内联字符串实际上是同一个对象。
例如:
String fooString1 = new String("foo");
String fooString2 = new String("foo");
// Evaluates to false
fooString1 == fooString2;
// Evaluates to true
fooString1.equals(fooString2);
// Evaluates to true, because Java uses the same object
"bar" == "bar";
但要小心空值!
==
可以很好地处理null
字符串,但是从空字符串调用.equals()
会导致异常:
String nullString1 = null;
String nullString2 = null;
// Evaluates to true
System.out.print(nullString1 == nullString2);
// Throws a NullPointerException
System.out.print(nullString1.equals(nullString2));
因此,如果您知道fooString1
可能为空,请通过写作告诉读者
System.out.print(fooString1 != null && fooString1.equals("bar"));
以下内容更短,但检查 null 的情况不太明显:
System.out.print("bar".equals(fooString1)); // "bar" is never null
System.out.print(Objects.equals(fooString1, "bar")); // Java 7 required
【讨论】:
有时看起来好像 "==" 比较值, --==
做 总是比较值! (只是某些值是引用!)
唉,isNullOrEmpty() 没有静态方法,也没有自定义运算符重载,这使得 Java 的这一部分比 C# 或 Python 更笨重。而且由于 Java 没有扩展方法,因此您无法编写自己的实用程序来扩展 java.lang.String。正确的?关于子类化 String,添加静态实用程序方法,然后总是使用 MyString 的任何想法?一个带有两个参数的静态方法用于进行空安全比较也会很好地包含在该子类中。
Groovy 使用 安全导航操作符 (groovy.codehaus.org/…)、?.
让这一切变得更容易。这会将nullString1?.equals(nullString2);
转换为完全空的语句。但是,如果您有 validString?.equals(nullString);
也无济于事——仍然会引发异常。
在 java 中比较可空字符串的短方法:***.com/questions/11271554/…
@JonCoombs Java 支持子类化和创建自己的方法。然而,由于某些原因,很少有类被标记为 final,String 就是其中之一,所以我们不能扩展。我们可以创建其他类并在那里创建实用程序类,它以两个字符串作为参数并在那里实现我们的逻辑。另外对于 null 检查其他一些库,例如 spring 和 apache,他收集了很好的方法,可以使用它。【参考方案9】:
我同意 zacherates 的回答。
但是你可以做的是在你的非文字字符串上调用intern()
。
来自 zacherates 示例:
// ... but they are not the same object
new String("test") == "test" ==> false
如果你实习非文字字符串相等是true
:
new String("test").intern() == "test" ==> true
【讨论】:
这通常不是一个好主意。实习相对昂贵,并且可以(自相矛盾地)>>增加== 进行字符串比较所带来的性能优势。【参考方案10】:Java 中的字符串是不可变的。这意味着每当您尝试更改/修改字符串时,您都会获得一个新实例。您不能更改原始字符串。这样做是为了可以缓存这些字符串实例。一个典型的程序包含大量的字符串引用,缓存这些实例可以减少内存占用并提高程序的性能。
当使用 == 运算符进行字符串比较时,您不是在比较字符串的内容,而是实际上是在比较内存地址。如果它们都相等,则返回 true ,否则返回 false 。而字符串中的equals比较字符串内容。
所以问题是如果所有的字符串都缓存在系统中,为什么==
返回false而equals返回true?嗯,这是可能的。如果你创建一个像String str = new String("Testing")
这样的新字符串,你最终会在缓存中创建一个新字符串,即使缓存已经包含一个具有相同内容的字符串。简而言之,"MyString" == new String("MyString")
将始终返回 false。
Java 还讨论了函数 intern() 可用于字符串以使其成为缓存的一部分,因此 "MyString" == new String("MyString").intern()
将返回 true。
注意:== 运算符比 equals 快得多,因为您正在比较两个内存地址,但您需要确保代码没有在代码中创建新的 String 实例。否则你会遇到错误。
【讨论】:
【参考方案11】:==
比较对象的引用值,而 java.lang.String
类中的 equals()
方法比较 String
对象的内容(与另一个对象)。
【讨论】:
不要挑剔,但String
的equals()
方法实际上在String
类中,而不是在Object
中。 Object
中的默认equals()
不会比较内容是否相同,实际上只是在引用相同时返回true。
@JacobSchoen :由于 GrepCode 已关闭,上述链接不再有效。这是 equals 实现的替代方法:[Inline Link] (zgrepcode.com/java/openjdk/10.0.2/java.base/java/lang/…)【参考方案12】:
所有对象都保证有一个.equals()
方法,因为Object 包含一个方法.equals()
,它返回一个布尔值。如果需要进一步定义,则覆盖此方法是子类的工作。没有它(即使用==
),只会检查两个对象之间的内存地址是否相等。 String 覆盖了这个.equals()
方法,而不是使用内存地址,它返回字符级别的字符串比较是否相等。
一个关键点是字符串存储在一个集中池中,因此一旦创建了字符串,它将永远存储在同一地址的程序中。字符串不会改变,它们是不可变的。这就是为什么如果您有大量的字符串处理要做,那么使用常规字符串连接是一个坏主意。相反,您将使用提供的StringBuilder
类。请记住,指向此字符串的指针可以更改,如果您有兴趣查看两个指针是否相同 ==
将是一个不错的方法。字符串本身没有。
【讨论】:
“一旦创建了字符串,它就会永远存储在同一地址的程序中” - 这是完全错误的。只有编译时常量字符串表达式(可能涉及final String
变量)和您的程序显式实习生的字符串存储在您所谓的“集总池”中。一旦没有更多对它们的实时引用,所有其他String
对象都会受到垃圾回收的影响,就像任何其他类型的对象一样。此外,虽然整个实习机制的工作需要不变性,但它与此无关。
字符串比较是通过 equals 或 equalsIgnoreCase 方法完成的,该方法实际上比较了字符串的内容。但是 == 符号只是检查参考值。对于这种情况下,来自字符串池的字符串文字可以正常工作。字符串 s1 = new String("a");字符串 s2 = new String("a");在这种情况下 s1==s2 为假,但 s1.equals(s2) 为真。【参考方案13】:
==
比较对象引用。
.equals()
比较字符串值。
有时==
会产生比较字符串值的错觉,如下情况:
String a="Test";
String b="Test";
if(a==b) ===> true
这是因为当您创建任何字符串字面量时,JVM 首先会在字符串池中搜索该字面量,如果找到匹配项,则会将相同的引用提供给新的字符串。因此,我们得到:
(a==b) ===> 真
String Pool
b -----------------> "test" <-----------------a
但是,==
在以下情况下会失败:
String a="test";
String b=new String("test");
if (a==b) ===> false
在这种情况下,对于new String("test")
,语句 new String 将在堆上创建,并且该引用将提供给 b
,因此 b
将在堆上而不是在字符串池中获得引用。
现在a
指向字符串池中的一个字符串,而b
指向堆上的一个字符串。因此我们得到:
如果(a==b)===> 错误。
String Pool
"test" <-------------------- a
Heap
"test" <-------------------- b
虽然 .equals()
总是比较 String 的值,所以它在两种情况下都为 true:
String a="Test";
String b="Test";
if(a.equals(b)) ===> true
String a="test";
String b=new String("test");
if(a.equals(b)) ===> true
所以使用.equals()
总是更好。
【讨论】:
.equals() 比较两个实例,但是实现 equals 来比较它们。这可能会或可能不会比较 toString 的输出。 @Jacob 对象类.equals()
方法比较实例(引用/地址),其中 String 类 .equals()
方法被覆盖以比较内容(字符)
很好地指出字符串池与 Java 堆的差异,因为它们肯定不相同。在字符串池中,Java 尝试“缓存”String
对象以节省内存占用,因为String
以不可变而著称(我希望我在这里说得对)。还要检查***.com/questions/3052442/…【参考方案14】:
我认为当您定义 String
时,您定义了一个对象。所以你需要使用.equals()
。当您使用原始数据类型时,您使用==
,但对于String
(和任何对象),您必须使用.equals()
。
【讨论】:
"char[]" 不是原始数据类型!这是一个“char”数组。而且数组本身并不是原始数据类型。【参考方案15】:功能:
public float simpleSimilarity(String u, String v)
String[] a = u.split(" ");
String[] b = v.split(" ");
long correct = 0;
int minLen = Math.min(a.length, b.length);
for (int i = 0; i < minLen; i++)
String aa = a[i];
String bb = b[i];
int minWordLength = Math.min(aa.length(), bb.length());
for (int j = 0; j < minWordLength; j++)
if (aa.charAt(j) == bb.charAt(j))
correct++;
return (float) (((double) correct) / Math.max(u.length(), v.length()));
测试:
String a = "This is the first string.";
String b = "this is not 1st string!";
// for exact string comparison, use .equals
boolean exact = a.equals(b);
// For similarity check, there are libraries for this
// Here I'll try a simple example I wrote
float similarity = simple_similarity(a,b);
【讨论】:
这与其他答案有何不同?以及为什么要按照您的建议进行操作 @Mark 关于==
和equals
之间区别的问题已经被其他解决方案回答了,我只是提供了一种不同的方法来松散地比较字符串【参考方案16】:
==
执行 reference 相等性检查,这两个对象(本例中为字符串)是否引用内存中的同一对象。
equals()
方法会检查两个对象的内容或状态是否相同。
显然==
更快,但如果您只想判断 2 个String
s 是否包含相同的文本,在许多情况下会(可能)给出错误结果。
绝对推荐使用equals()
方法。
不用担心性能。鼓励使用String.equals()
的一些事情:
String.equals()
的实现首先检查引用是否相等(使用==
),如果两个字符串通过引用相同,则不再进行计算!
如果两个字符串引用不相同,String.equals()
将接下来检查字符串的长度。这也是一个快速操作,因为String
类存储了字符串的长度,无需计算字符或代码点。如果长度不同,则不会执行进一步检查,我们知道它们不可能相等。
只有到了这一步,才会真正比较两个字符串的内容,这将是一个简写比较:如果我们发现一个不匹配的字符(在同一位置2 个字符串),将不再检查其他字符。
说到底,即使我们保证字符串是实习生,使用equals()
方法仍然不是人们想象的那么开销,绝对是推荐的方法。如果你想要一个有效的引用检查,那么在语言规范和实现保证相同的枚举值将是相同的对象(通过引用)的情况下使用枚举。
【讨论】:
Obviously == is faster
-- 实际上.equals(String)
的实现首先检查==
,所以我会说速度大致相同。
public boolean equals(Object anObject) if (this == anObject) return true; ...
【参考方案17】:
Java 有一个字符串池,Java 在该池下管理字符串对象的内存分配。见String Pools in Java
当您使用==
运算符检查(比较)两个对象时,它会将地址相等性与字符串池进行比较。如果两个 String 对象具有相同的地址引用,则返回 true
,否则返回 false
。但是如果你想比较两个 String 对象的内容,那么你必须重写 equals
方法。
equals
实际上是Object类的方法,但是被重写到String类中,并给出了一个新的定义来比较object的内容。
Example:
stringObjectOne.equals(stringObjectTwo);
但请注意,它尊重字符串的大小写。如果您想要不区分大小写的比较,那么您必须使用 String 类的 equalsIgnoreCase 方法。
让我们看看:
String one = "HELLO";
String two = "HELLO";
String three = new String("HELLO");
String four = "hello";
one == two; // TRUE
one == three; // FALSE
one == four; // FALSE
one.equals(two); // TRUE
one.equals(three); // TRUE
one.equals(four); // FALSE
one.equalsIgnoreCase(four); // TRUE
【讨论】:
我看到这是对大问题的迟到回答。我可以问一下现有答案中没有提到的它提供了什么吗? @Mysticial 他添加了equalsIgnoreCase
,这可能对新人有用。【参考方案18】:
运算符 == 始终用于对象引用比较,而 String 类 .equals() 方法被 覆盖内容比较:
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2); // It prints false (reference comparison)
System.out.println(s1.equals(s2)); // It prints true (content comparison)
【讨论】:
【参考方案19】:您还可以使用compareTo()
方法来比较两个字符串。如果 compareTo 结果为 0,则两个字符串相等,否则被比较的字符串不相等。
==
比较引用而不比较实际字符串。如果您确实使用new String(somestring).intern()
创建了每个字符串,那么您可以使用==
运算符来比较两个字符串,否则只能使用equals() 或compareTo 方法。
【讨论】:
【参考方案20】:如果你和我一样,当我第一次开始使用 Java 时,我想使用“==”运算符来测试两个 String 实例是否相等,但无论好坏,这都不是正确的方法在 Java 中。
在本教程中,我将演示几种正确比较 Java 字符串的不同方法,从我大部分时间使用的方法开始。在本 Java 字符串比较教程结束时,我还将讨论为什么“==”运算符在比较 Java 字符串时不起作用。
选项 1:Java 字符串与 equals 方法的比较 大多数时候(可能是 95% 的时间)我用 Java String 类的 equals 方法比较字符串,像这样:
if (string1.equals(string2))
此 String equals 方法查看两个 Java 字符串,如果它们包含完全相同的字符串,则认为它们相等。
看一个使用equals方法的快速字符串比较示例,如果运行以下测试,两个字符串将不会被认为是相等的,因为字符不完全相同(字符大小写不同) :
String string1 = "foo";
String string2 = "FOO";
if (string1.equals(string2))
// this line will not print because the
// java string equals method returns false:
System.out.println("The two strings are the same.")
但是,当两个字符串包含完全相同的字符串时,equals 方法将返回 true,如下例所示:
String string1 = "foo";
String string2 = "foo";
// test for equality with the java string equals method
if (string1.equals(string2))
// this line WILL print
System.out.println("The two strings are the same.")
选项 2:使用 equalsIgnoreCase 方法进行字符串比较
在某些字符串比较测试中,您会希望忽略字符串是大写还是小写。当您想以这种不区分大小写的方式测试字符串是否相等时,请使用 String 类的 equalsIgnoreCase 方法,如下所示:
String string1 = "foo";
String string2 = "FOO";
// java string compare while ignoring case
if (string1.equalsIgnoreCase(string2))
// this line WILL print
System.out.println("Ignoring case, the two strings are the same.")
选项 3:使用 compareTo 方法比较 Java 字符串
还有第三种比较不常用的方法来比较 Java 字符串,那就是使用 String 类的 compareTo 方法。如果两个字符串完全相同,则 compareTo 方法将返回值 0(零)。下面是这个字符串比较方法的一个简单示例:
String string1 = "foo bar";
String string2 = "foo bar";
// java string compare example
if (string1.compareTo(string2) == 0)
// this line WILL print
System.out.println("The two strings are the same.")
虽然我正在写 Java 中的这种相等概念,但重要的是要注意 Java 语言在基本 Java Object 类中包含一个 equals 方法。每当您创建自己的对象并且想要提供一种方法来查看对象的两个实例是否“相等”时,您应该在您的类中覆盖(并实现)这个 equals 方法(以 Java 语言提供的相同方式String equals 方法中的这种相等/比较行为)。
你可能想看看这个==, .equals(), compareTo(), and compare()
【讨论】:
对于字符串字面量,例如 String string1 = "foo bar"; String string2 = "foo bar";您可以直接使用 == 运算符来测试内容相等性 在谷歌应用脚本“compareTo”是不可能的。我试过 instaed "equals" 这是唯一有效的解决方案....【参考方案21】:==
运算符检查两个引用是否指向同一个对象。 .equals()
检查实际字符串内容(值)。
注意.equals()
方法属于Object
类(所有类的超类)。您需要根据您的类要求覆盖它,但对于 String 它已经实现,它会检查两个字符串是否具有相同的值。
案例 1
String s1 = "Stack Overflow";
String s2 = "Stack Overflow";
s1 == s2; //true
s1.equals(s2); //true
原因:创建的不带 null 的字符串字面量存储在堆的 permgen 区域的字符串池中。所以 s1 和 s2 都指向池中的同一个对象。
案例 2
String s1 = new String("Stack Overflow");
String s2 = new String("Stack Overflow");
s1 == s2; //false
s1.equals(s2); //true
原因:如果您使用 new
关键字创建一个 String 对象,则会在堆上为其分配一个单独的空间。
【讨论】:
【参考方案22】:如果equals()
方法存在于java.lang.Object
类中,并且它应该检查对象状态的等价性!这意味着,对象的内容。而==
运算符应检查实际对象实例是否相同。
例子
考虑两个不同的引用变量,str1
和 str2
:
str1 = new String("abc");
str2 = new String("abc");
如果您使用equals()
System.out.println((str1.equals(str2))?"TRUE":"FALSE");
如果您使用==
,您将获得TRUE
的输出。
System.out.println((str1==str2) ? "TRUE" : "FALSE");
现在您将得到FALSE
作为输出,因为str1
和str2
都指向两个不同的对象,即使它们共享相同的字符串内容。这是因为new String()
每次都会创建一个新对象。
【讨论】:
【参考方案23】:在 Java 中,当 ==
运算符用于比较 2 个对象时,它会检查对象是否引用内存中的同一位置。换句话说,它会检查这 2 个对象名称是否基本上是对同一内存位置的引用。
Java String
类实际上覆盖了 Object
类中的默认 equals()
实现——它覆盖了该方法,因此它只检查字符串的值,而不是它们在内存中的位置。
这意味着如果调用equals()
方法比较2个String
对象,那么只要实际的字符序列相等,两个对象就被认为相等。
==
运算符检查两个字符串是否完全相同。
.equals()
方法检查两个字符串是否具有相同的值。
【讨论】:
除非其中一个为空,因为如果 s 为空,s.equals(s2) 将崩溃,导致比较失败。当然,这与答案并不矛盾。这只是一个警告。 不,它不会崩溃,它会抛出 NullPointerException,导致比较不发生。以上是关于如何比较 Java 中的字符串?的主要内容,如果未能解决你的问题,请参考以下文章