Сравните два объекта в Java с возможными нулевыми значениями

189

Я хочу сравнить две строки на равенство в Java, когда одна или обе могут быть null, поэтому я не могу просто позвонить .equals(). Какой самый лучший способ?

boolean compare(String str1, String str2) {
    ...
}

Редактировать:

return ((str1 == str2) || (str1 != null && str1.equals(str2)));
Priceline
источник

Ответы:

173

Вот что использует внутренний код Java (для других compareметодов):

public static boolean compare(String str1, String str2) {
    return (str1 == null ? str2 == null : str1.equals(str2));
}
Эрик
источник
3
Почему бы просто не изменить типы String на Objects - сделать их более общими? И тогда это то же самое, что вы получите, если перейдете на Java 7.
Tom
3
Какой класс Java содержит метод, который вы предоставили?
Владимир Левицкий
30
Привет! у вас не должно быть метода сравнения, возвращающего true / false. Равно не то же самое, что сравнить. Сравнение должно быть полезно для сортировки. Вы должны вернуться <0, ==0или >0указать , какой из них меньше / терки , чем другой
Coya
10
я предлагаю использовать, Objects.equals(Object, Object)как указал Марк Роттвил в своем ответе (пожалуйста, проголосуйте за него)
Нейрон
1
@ Эрик, который на самом деле не делает его менее хорошим ответом. Я уверен, что вы не станете утверждать, что лучшим ответом всегда будет тот, который содержит только код, который совместим с каждой существующей java-версией
Neuron
318

Начиная с Java 7 вы можете использовать статический метод java.util.Objects.equals(Object, Object)для выполнения проверки на равенство двух объектов, не заботясь о том, что они есть null.

Если оба объекта будут, nullон вернется true, если один будет, nullа другой нет, он вернется false. В противном случае он вернет результат вызова equalsпервого объекта со вторым в качестве аргумента.

Марк Роттвил
источник
этот подход откладывает проверку типа до времени выполнения программы. два последствия: это будет медленнее во время выполнения, и IDE не сообщит вам, если вы случайно попытаетесь сравнить разные типы.
Авераско
10
@averasko При использовании Object.equals(Object)нет проверки типа времени компиляции. Objects.equals(Object, Object)Работает очень так же , как нормальный equals, и повторно умозаключение / проверки по IDE, эти правила эвристики, которые также поддерживаются для Objects.equals(Object, Object)(например , IntelliJ IDEA 2016.2 имеет ту же проверку для обоих нормальных равных и один из объектов).
Марк Роттвил
2
Мне нравится этот подход. В наши дни не нужно включать библиотеку ApacheCommons.
Торстен Оджаперв
1
@SimonBaars Это не запуск функции на нулевом объекте, это статический метод, в котором вы передаете два аргумента, которые могут быть нулевыми, или ссылку на объект.
Марк Роттвил
8
Имхо это должен быть принятый ответ. Я не хочу изобретать велосипед в каждом приложении, которое я пишу
Neuron
45

Для этих случаев было бы лучше использовать Apache Commons StringUtils # equals , он уже обрабатывает нулевые строки. Пример кода:

public boolean compare(String s1, String s2) {
    return StringUtils.equals(s1, s2);
}

Если вы не хотите , чтобы добавить библиотеку, просто скопируйте исходный код этого StringUtils#equalsметода и применять его , когда вам это нужно.

Луиджи Мендоса
источник
29

Для тех на Android, кто не может использовать API 19 Objects.equals (str1, str2), есть это:

android.text.TextUtils.equals(str1, str2);

Это абсолютно безопасно. Ему редко приходится использовать более дорогой метод string.equals (), поскольку идентичные строки на андроиде почти всегда сравнивают true с операндом "==" благодаря Android String Pooling , а проверки длины - это быстрый способ отфильтровать большинство несовпадений.

Исходный код:

/**
 * Returns true if a and b are equal, including if they are both null.
 * <p><i>Note: In platform versions 1.1 and earlier, this method only worked  well if
 * both the arguments were instances of String.</i></p>
 * @param a first CharSequence to check
 * @param b second CharSequence to check
 * @return true if a and b are equal
 */
public static boolean equals(CharSequence a, CharSequence b) {
    if (a == b) return true;
    int length;
    if (a != null && b != null && (length = a.length()) == b.length()) {
        if (a instanceof String && b instanceof String) {
            return a.equals(b);
        } else {
            for (int i = 0; i < length; i++) {
                if (a.charAt(i) != b.charAt(i)) return false;
            }
            return true;
        }
    }
    return false;
}
NameSpace
источник
7

Начиная с версии 3.5 Apache Commons StringUtils имеет следующие методы:

static int  compare(String str1, String str2)
static int  compare(String str1, String str2, boolean nullIsLess)
static int  compareIgnoreCase(String str1, String str2)
static int  compareIgnoreCase(String str1, String str2, boolean nullIsLess)

Они обеспечивают нулевое безопасное сравнение строк.

НЗП
источник
6

Используя Java 8:

private static Comparator<String> nullSafeStringComparator = Comparator
        .nullsFirst(String::compareToIgnoreCase); 

private static Comparator<Metadata> metadataComparator = Comparator
        .comparing(Metadata::getName, nullSafeStringComparator)
        .thenComparing(Metadata::getValue, nullSafeStringComparator);

public int compareTo(Metadata that) {
    return metadataComparator.compare(this, that);
}
TanvirChowdhury
источник
4

Сравните две строки, используя метод equals (-, -) и equalsIgnoreCase (-, -) класса StringUtils Apache Commons.

StringUtils.equals (-, -) :

StringUtils.equals(null, null)   = true
StringUtils.equals(null, "abc")  = false
StringUtils.equals("abc", null)  = false
StringUtils.equals("abc", "abc") = true
StringUtils.equals("abc", "ABC") = false

StringUtils.equalsIgnoreCase (-, -) :

StringUtils.equalsIgnoreCase(null, null)   = true
StringUtils.equalsIgnoreCase(null, "abc")  = false
StringUtils.equalsIgnoreCase("xyz", null)  = false
StringUtils.equalsIgnoreCase("xyz", "xyz") = true
StringUtils.equalsIgnoreCase("xyz", "XYZ") = true
Прашант Саху
источник
3

Вы можете использовать java.util.Objectsследующим образом.

public static boolean compare(String str1, String str2) {
    return Objects.equals(str1, str2);
}
Джоби Субрамунян
источник
2
boolean compare(String str1, String str2) {
    if(str1==null || str2==null) {
        //return false; if you assume null not equal to null
        return str1==str2;
    }
    return str1.equals(str2);
}

это то, что вы хотели?

didxga
источник
5
Этот случай возвращается, falseесли обе строки nullне являются желаемым результатом.
JScoobyCed
1
boolean compare(String str1, String str2) {
    if (str1 == null || str2 == null)
        return str1 == str2;

    return str1.equals(str2);
}
Kierrow
источник
1
boolean compare(String str1, String str2) {
  return (str1==null || str2==null) ? str1 == str2 : str1.equals(str2);
}
Сумит Сингх
источник
0

Итак, что означает «наилучшее возможное решение»?

Если вы имеете в виду наиболее читаемый, то все возможные решения в значительной степени эквивалентны для опытного программиста на Java. Но самое читаемое ИМО это

 public boolean compareStringsOrNulls(String str1, String str2) {
     // Implement it how you like
 }

Другими словами, скрыть реализацию внутри простого метода, который (в идеале) может быть встроен.

(Вы также можете использовать исходный код для сторонней служебной библиотеки ... если вы уже используете ее в своей кодовой базе.)


Если вы имеете в виду наиболее производительный, то:

  1. наиболее эффективное решение зависит от платформы и контекста,
  2. одной из проблем «контекста» является относительная (динамическая) частота появления nullаргументов,
  3. это , вероятно , не имеет значения , какая версия работает быстрее ... потому что разница, вероятно , слишком мал , чтобы сделать разницу в общую производительность приложений, а также
  4. если это имеет значение, единственный способ выяснить, какой самый быстрый на вашей платформе, это попробовать обе версии и измерить разницу.
Стивен С
источник