Проверка равенства трех или более строк в Java

Я знаю, что это очень простой вопрос, но я всегда пытаюсь найти способы сделать мой код чистым и лаконичным, насколько это возможно. Есть ли способ сравнить равенство трех или более строк в одном выражении if()? В настоящее время я использую оператор && для постепенного сравнения каждой строки. Однако, как вы могли себе представить, между длинными именами переменных и вызываемыми методами эти операторы if() очень сильно засоряются. Кроме того, я планирую иметь неизвестное количество этих строк и хотел бы избежать сложного цикла for с загроможденным if() вложенным внутри них. Вот как выглядит мой код:

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();
}

Ответ 1

Нет простого способа связать ваши команды equals() следующим образом. Чтобы иметь возможность связывать их таким образом, equals() должен был бы вернуть ссылку на сам String. Затем, однако, он не может вернуть логическое значение, которое представляет результат сравнения больше.

Кроме того, я считаю, что не особенно проблематично сравнивать три строки отдельно, как в первом примере. Убедитесь, что ваш код хорошо отформатирован, и он будет оставаться понятным даже для более длинных имен переменных:

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

В качестве альтернативы вы можете использовать одно из других решений, предложенных в этом потоке, и перенести свои значения в коллекцию и написать вспомогательный метод. Обратите внимание, однако, что это может иметь негативное влияние на использование памяти, производительность и, возможно, читабельность.


На боковой ноте вы никогда не должны создавать строки с помощью конструктора new String(). Просто присвойте строковый литерал непосредственно своей переменной:

String a = "a";

Ответ 2

Если у вас есть несколько объектов одного типа, рекомендуется организовать их в структуре данных, например массиве или списке. Предположим, что у вас есть List объектов String:

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
}

Ответ 3

Существуют различные способы решения этой проблемы в зависимости от количества сравниваемых объектов 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) { }

Ответ 4

Кроме того, я планирую иметь неизвестное количество этих строк, и хотел бы избежать сложного для циклов с загроможденными if() s вложенными внутри них.

Я не думаю, что цикл for был бы таким сложным.

Вы можете, конечно, проверить равенство, попробовав

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

Но сравнение всего с a будет иметь тот же результат. Если все равно a, все равно друг другу.

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

Это упрощает цикл for. Если вы не против накладных расходов на сравнение a с самим собой, вы можете просто проверить весь свой массив или коллекцию (или что-то еще) против равенства a.

Если не выглядит загроможденным. return из функции, как только одна пара строк не равна.

Ответ 5

Это может быть необычная идея, но ситуации, подобные этому, - это небольшое метапрограммирование, которое может пригодиться.

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);
}

Вы можете генерировать столько перегрузок, сколько хотите, нет необходимости в varargs. Просто скопируйте и вставьте в класс утилиты, и все готово.

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

Для такого простого генерации кода край ошибки является небольшим. Единственным недостатком является ремонтопригодность, потому что если кому-то когда-либо понадобилось возиться с ним (например, чтобы изменить обработку нулевого значения, которое игнорируется как в моем, так и в ваших примерах), им необходимо изменить генератор и запустить его снова.

Ответ 6

Как насчет чего-то вроде этого:

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;
}

Не совсем красиво, но вам нужно написать его только один раз, и он работает для произвольного количества строк.

Ответ 7

Еще один способ - использовать 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.