在Java中检查三个或更多字符串的相等性[重复]

Posted

技术标签:

【中文标题】在Java中检查三个或更多字符串的相等性[重复]【英文标题】:Checking equality of three or more Strings in Java [duplicate] 【发布时间】:2015-08-14 03:15:32 【问题描述】:

我知道这是一个非常基本的问题,但我一直在努力寻找使我的代码尽可能干净简洁的方法。有没有办法在单个if() 语句中比较三个或更多字符串的相等性?我目前正在使用&& 运算符逐步比较每个字符串。但是,正如您可以想象的那样,在长变量名和被调用的方法之间,这些if() 语句很快就会变得非常混乱。此外,我计划拥有未知数量的这些字符串,并希望避免复杂的for 循环,其中嵌套了杂乱的if()s。这是我的代码目前的样子:

String a = new String("a");
String b = new String("b");
String c = new String("c");
if(a.equals(b) && b.equals(c)) 
    doSomething();

有没有一种方法,或者我可以使用的某种集合,可以让我更像这样比较这些值:

if(a.equals(b).equals(c)) 
    doSomething();

【问题讨论】:

@RoberKrupp 我刚刚发布了一个答案,它为您提供了世界上最好的答案。请看一下,如果您需要任何说明,请告诉我。 你听说过函数吗?你可以编写函数来做一些事情,就像你想要的任何东西。然后你可以打电话给他们,这有多酷?您甚至可以将其命名为 allStringsEqual 之类的名称,以便其他人知道该函数的作用。整洁,对吧? @aaa90210 严格来说 Java 没有函数。它有methods。 【参考方案1】:

没有像这样链接equals() 命令的简单方法。为了能够以这种方式链接它们,equals() 必须返回对 String 本身的引用。但是,它不能再返回表示比较结果的布尔值。

另外,我认为不是特别成问题,像在您的第一个示例中那样分别比较三个字符串。确保您的代码格式正确,即使变量名较长,也易于理解:

if(myValueAWithALongName.equals(myValueBWithALongName) &&
   myValueBWithALongName.equals(myValueCWithALongName) &&
   myValueCWithALongName.equals(myValueDWithALongName) &&
   ... ) 

或者,您可以使用此线程中提出的其他解决方案之一,并将您的值包装到 Collection 中并编写一个辅助方法。但请注意,这可能会对内存使用、性能和可能的可读性产生负面影响。


附带说明,您永远不应该使用构造函数new String() 创建字符串。只需将字符串文字直接分配给您的变量:

String a = "a";

【讨论】:

@TimeSta 您已选择忽略null 检查,这使得if 条件看起来可以接受。添加null 检查,您很快就会看到代码变得多么不可读。更好的选择是将if 条件包装在一个方法中,然后在主if 条件中调用该单一方法。此外,三个String 对象也可以。想象一下,当您只需要比较 5 个String 对象时会发生什么?看我的回答.. @ChetanKinger 如果需要更通用的解决方案或空值检查,那么您的解决方案肯定会优于我的解决方案。但是,在这种情况下,OP 没有要求进行空检查,考虑到示例代码也没有必要进行检查。在这种情况下,我更喜欢直接在 if 子句中使用多个条件而不是辅助方法,但这取决于个人喜好。两种解决方案对内存使用、性能和可维护性的影响不会有太大差异。 很公平。根据示例代码,空值检查可能不是强制性的,但我怀疑它们在现实世界中是否可以忽略。此外,OP 显然正在寻找一种更简洁的解决方案。将 3 个或更多 if 条件包装到具有有意义名称的方法中并在主 if 语句中调用该方法肯定会使 if 条件易于阅读。我觉得不能因为个人风格而放弃这个优势。 @ChetanKinger 使用这种方法时,您只需要对第一个字符串进行一次空检查。并且使用对称的Objects.equals 甚至可以避免这种检查。 @CodesInChaos 是的。正如我的回答中已经提到的那样,使用 Objects.equals 将是理想的。【参考方案2】:

这样的事情怎么样:

boolean allEqual(List<String> elements) 
    if(elements.isEmpty() || elements.size() == 1) 
        return true;
    
    String current = elements.get(0);
    if(current == null) 
        return false;
    
    for(int i = 1; i < elements.size(); i++) 
        if(!current.equals(elements.get(i))) 
            return false;
        
        current = elements.get(i);
    
    return true;

不完全漂亮,但你只需要编写一次,它适用于任意数量的字符串。

【讨论】:

此代码将在null 值的情况下中断。 @ChetanKinger 好点,我添加了一个空检查。 如果所有值都为空,则该空检查仍将失败。这取决于设计者和此示例的用例,但如果所有字符串都为空,您会怎么做?它们是相等的,所以你不应该返回 true 吗? @MatthewC:我同意,但这确实是一个设计决定。我想尽量减少这个例子。 @niceguy 这不是设计决定。这实际上是一个实施决策。 Objects.equals 是一个很好的实用方法,可以让您的工作变得简单。看我的回答。【参考方案3】:

如果您有多个相同类型的对象,最好将它们组织成数组或列表等数据结构。所以假设你有一个ListString 对象:

List<String> strings = Arrays.asList("a", "b", "a", ...);

您想知道列表中的所有字符串是否彼此相等。这可以通过多种方法来完成。可能最短的是:

if(new HashSet<>(strings).size() == 1) 
    // all strings are equal

@niceguy 提出了更长但更优化的解决方案。如果你使用的是 Java-8,你也可以这样做:

if(strings.stream().distinct().count() == 1) 
    // all strings are equal

【讨论】:

这是一种非常干净的方法。虽然,HashSet 中使用的散列算法引入了不必要的开销 @Tagir Valeev 我真的很喜欢你的解决方案多么简洁,但是,作为一个或多或少自学成才的程序员,我不太熟悉像“HashSet”这样的高阶数据结构你知道吗我可以利用哪些资源来学习这些概念?我已经尝试过查看 java 文档,但是我相信您已经知道这些信息的密集程度(或者我什至敢说令人生畏)。 计算所有唯一字符串的所有出现是安静的一些开销,distinct().count() 基本上与幕后的new HashSet&lt;&gt;(strings).size() 相同。另一方面,strings.isEmpty() || strings.stream().allMatch(strings.get(0)::equals) 的长度并没有那么长,而且总体上是短路且更轻量级的。如果null 应该被处理,strings.isEmpty() || strings.stream().allMatch(Predicate.isEqual(strings.get(0))) 将是要走的路。【参考方案4】:

另一种方法是使用String 作为Comparable 的实现。

public <T extends Comparable<T>> boolean equality(T... objects) 
    Arrays.sort(objects);
    return objects[0].compareTo(objects[objects.length - 1]) == 0;

优点: 此解决方案适用于所有 Comparable 对象。

缺点:如果所有值都等于N(太棒了!)比较,否则N logN max。这里还将为sort 方法分配内存用于临时存储,该方法从非常小到N/2 不等。

【讨论】:

我不确定这是否是最佳解决方案。排序将是O(n*log(n)),而简单的迭代和比较第一个元素与其余元素将是O(n) 我并不是说这是最好的解决方案。再来一张。【参考方案5】:

根据要比较的String 对象的数量,有不同的方法可以解决此问题。

如果您只有几个 String 对象要比较,并且您几乎需要在应用程序中的一两个地方进行这种比较,您可以将代码包装在需要此类的类中的方法中一个比较,使代码变得更具可读性:

 public boolean isConditionSatisfied(String a,String b,String c) 
    return Objects.equals(a,b) && Objects.equals(b,c);
 

然后客户端代码被简化并且更具可读性:

if(isConditionSatisfied(a,b,c))  //doSomething 

请注意,Objects.equals 是自 JDK 1.7 起可用的内置方法。与其他答案相比,使用Object.equals 可以保护您免受NullPointerExcsption 的影响,我认为这是绝对必要的。

如果您发现自己经常需要这种比较(不仅针对String 对象),为什么还要寻找仅用于比较String 对象的解决方案?为什么不找到适用于任何种类或数量的对象的通用解决方案?

public class ObjectUtils 

    public static boolean multipleEquals(Object... objectsToCompare) 

        if (null == objectsToCompare) 
            return false;
         else if (objectsToCompare.length == 0) 
            return false;
         else if (objectsToCompare.length == 1)      
            return true;
        

        boolean allEqual = true;
        for (int curr = 1; curr < objectsToCompare.length; ++curr) 
            if(!Objects.equals(objectsToCompare[0],objectsToCompare[curr])) 
                allEqual = false;
                break;
            
        

        return allEqual;
    

好的,你想比较 4 个 String 对象,为什么不呢:

if(ObjectUtils.multipleEquals("1","1","1","1")  

但现在你想比较 5 个Integer 对象,没问题:

if(ObjectUtils.multipleEquals(1,2,3,4,5)  

你甚至可以混合和匹配对象:

if(ObjectUtils.multipleEquals("1",2,"3",4)  

【讨论】:

您不需要将最后一项与第一项进行比较。这简化了代码。 我不知道这是否是他们投反对票的原因,但老实说,您的代码有点笨拙。作为参考,Jesper 有一个非常整洁的here。 @Radiodef Jesper 确实有一个简洁的实现,但是代码会在第一个空符号出现时中断,在我看来这是一件坏事。我的答案中的代码只是指示性的,可以根据自己的喜好进行改进。我的回答涵盖了很多优点。使用包装器方法,使用 Objects.equals 和在 null 值的情况下不会中断的可靠代码等等。不知道为什么用户觉得让我有机会改进涵盖这么多优点的答案并不是一个真正的选择?我已编辑我的答案以删除不必要的检查。 我已经编辑了答案中的代码。如果反对者可以发表评论并让我知道他们认为我的回答是否需要任何其他改进,那就太好了。不工作的干净代码是没有意义的。如果一个胭脂 null 值,其他答案中的代码将主要中断。 (我完全反对有人对答案投反对票,除非它明显不正确。这就是为什么我选择让 cmets 保留其他答案而不是对他们投反对票,即使他们中的大多数人在现实世界中都会失败) @ChetanKinger 我可能会缩短如果检查,他们觉得多余。 - 如果有人觉得他需要调用不带参数的方法,抛出异常是完全可以接受的,这可以记录下来,代码会变得更清晰,我认为用空数组调用这个函数是错误的,所以它应该被抛出。【参考方案6】:

此外,我计划拥有未知数量的这些字符串, 并且希望避免复杂的嵌套 if() 的复杂 for 循环 在他们里面。

我认为 for 循环不会那么复杂。

你当然可以通过尝试来检查是否相等

a.equals(b)
b.equals(c)
etc.

但是将所有内容与a 进行比较将得到相同的结果。如果一切都等于a,那么一切都彼此相等。

a.equals(b)
a.equals(c)
etc.

这使得 for 循环更简单。如果您不介意将 a 与自身进行比较的开销,您可以简单地检查整个数组或集合(或其他)是否相等。

if 看起来也不混乱。 return 在一对字符串不相等时立即从函数中返回。

【讨论】:

【参考方案7】:

这可能是一个不寻常的想法,但在这种情况下,一点元编程可能会派上用场。

public static void nEquals(int nOverloads, PrintStream out) 
    if (nOverloads < 2)
        throw new IllegalArgumentException();

    for (int i = 2; i <= nOverloads; ++i) 
        out.println("public static boolean equals(");
        out.print("        Object obj0");

        for (int j = 1; j < i; ++j) 
            out.print(", Object obj" + j);
        

        out.println(") ");
        out.print("    return obj0.equals(obj1)");

        for (int j = 2; j < i; ++j) 
            out.print(" && obj0.equals(obj" + j + ")");
        

        out.println(";");
        out.println("");
    

nEquals(4, System.out); 打印以下内容:

public static boolean equals(
        Object obj0, Object obj1) 
    return obj0.equals(obj1);

public static boolean equals(
        Object obj0, Object obj1, Object obj2) 
    return obj0.equals(obj1) && obj0.equals(obj2);

public static boolean equals(
        Object obj0, Object obj1, Object obj2, Object obj3) 
    return obj0.equals(obj1) && obj0.equals(obj2) && obj0.equals(obj3);

您可以根据需要生成任意数量的重载,无需可变参数。只需复制并粘贴到实用程序类即可。

if (Util.equals(a, b, c)) 
    doSomething();

对于这样一个简单的代码生成,出错的余地很小。唯一的缺点是可维护性,因为如果有人确实需要修改它(例如更改 null 的处理,这在我和您的示例中都被忽略了),他们需要修改生成器并再次运行它。

【讨论】:

这不是varargs的重点吗? @Teepeemm 当然可以,但是 varargs 是一个数组。如果您只想要一些重载但又想确保它们都相同,还有另一种方法。 @Radiodef 我真的很喜欢这个解决方案。这可以成为一个非常好的 eclipse 插件。向我 +1,即使我对这个问题有一个答案,这个问题正被反对票轰炸:)。话虽如此,我仍然觉得null 检查是绝对必要的,因为会有一个胭脂客户肯定会在某个地方输入null。为什么不按照我的回答中的建议使用Objects.equals

以上是关于在Java中检查三个或更多字符串的相等性[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何检查NaN javascript的相等性[重复]

检查两个数组之间的相等性[重复]

如何检查 Javascript 中 Unicode 字符串的相等性?

比较三个(或更多)字典并在至少两个相等时找到匹配项

Java if 语句在同时具有赋值和相等性检查 OR - d 时如何工作? [复制]

Java 中字两个字符串判断是否相等(转载)