Porównaj dwa obiekty w Javie z możliwymi wartościami zerowymi

189

Chcę porównać dwa ciągi znaków równości w Javie, gdy jedno lub oba mogą być null, więc nie mogę po prostu wywołać .equals(). Jaka jest najlepsza droga?

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

Edytować:

return ((str1 == str2) || (str1 != null && str1.equals(str2)));
cennik
źródło
możliwy duplikat
Lepszego

Odpowiedzi:

173

Oto, czego używa wewnętrzny kod Java (na innych comparemetodach):

public static boolean compare(String str1, String str2) {
    return (str1 == null ? str2 == null : str1.equals(str2));
}
Eric
źródło
3
Dlaczego nie zmienić typów ciągów na Obiekty - czyniąc je bardziej ogólnym? A potem to samo, co dostaniesz, jeśli przejdziesz do Java 7.
Tom
3
Która klasa java zawiera podaną metodę?
Volodymyr Levytskyi
30
Hej! nie powinieneś mieć metody porównywania zwracającej true / false. Równa się to nie to samo co porównanie. Porównaj powinno być przydatne do sortowania. Należy zwrócić <0, ==0lub >0w celu wskazania, który z nich jest / tarka niższy niż w innych
Coya
10
proponuję użyć, Objects.equals(Object, Object)jak wskazał Mark Rotteveel w swojej odpowiedzi (proszę głosować)
Neuron
1
@Eric, co tak naprawdę nie czyni go mniej dobrą odpowiedzią. Jestem pewien, że nie spierasz się, że najlepszą odpowiedzią jest zawsze ten zawierający tylko kod, który jest kompatybilny z każdą wersją java, która zawsze istniała
Neuron
316

Od wersji Java 7 można używać metody statycznej java.util.Objects.equals(Object, Object)do przeprowadzania kontroli równości na dwóch obiektach bez dbania o nie null.

Jeśli oba obiekty są null, zwróci true, jeśli jeden jest, nulla drugi nie, to zwróci false. W przeciwnym razie zwróci wynik wywołania equalspierwszego obiektu z drugim argumentem.

Mark Rotteveel
źródło
takie podejście odracza sprawdzanie typu do czasu uruchomienia programu. dwie konsekwencje: w czasie wykonywania będzie wolniej, a IDE nie poinformuje cię, jeśli przypadkiem spróbujesz porównać różne typy.
averasko
10
@averasko Podczas korzystania Object.equals(Object)nie ma sprawdzania typu czasu kompilacji. Objects.equals(Object, Object)Działa bardzo podobnie jak normalne equals, a ponowne wnioskowanie / kontrole przeprowadzane przez IDE, zasady te są heurystyki, które również są obsługiwane przez Objects.equals(Object, Object)(np IntelliJ IDEA 2016.2 ma taką samą kontrolę zarówno dla normalnych rówieśników i jednego z obiektów).
Mark Rotteveel
2
Lubię to podejście. W dzisiejszych czasach nie trzeba dołączać biblioteki ApacheCommons.
Torsten Ojaperv
1
@SimonBaars To nie jest uruchamianie funkcji na obiekcie o wartości null, jest to metoda statyczna, w której przekazujesz dwa argumenty, które mogą być null, lub odwołanie do obiektu.
Mark Rotteveel,
8
imho to powinna być zaakceptowana odpowiedź. Nie chcę na nowo wymyślać koła w każdej aplikacji, którą piszę
Neuron
45

W takich przypadkach lepiej byłoby użyć Apache Commons StringUtils # równa się , obsługuje już ciągi zerowe. Przykładowy kod:

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

Jeśli dont chcesz dodać do biblioteki, wystarczy skopiować kod źródłowy z StringUtils#equalsmetodą i stosuje się go, gdy jest to potrzebne.

Luiggi Mendoza
źródło
28

Dla Androida, który nie może korzystać z Objects.equals API 19's (str1, str2):

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

To jest bezpieczne. Rzadko musi używać droższej metody string.equals (), ponieważ identyczne ciągi w Androidzie prawie zawsze porównują prawdę z operandem „==” dzięki puli ciągów w Androidzie , a sprawdzanie długości to szybki sposób na odfiltrowanie większości niedopasowań.

Kod źródłowy:

/**
 * 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
źródło
7

Od wersji 3.5 Apache Commons StringUtils ma następujące metody:

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)

Zapewniają one zerowe bezpieczne porównywanie ciągów.

phn
źródło
6

Za pomocą 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
źródło
4

Porównaj dwa ciągi za pomocą metody equals (-, -) i equalsIgnoreCase (-, -) klasy Apache Commons StringUtils.

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
Prashant Sahoo
źródło
3

Możesz użyć java.util.Objectsw następujący sposób.

public static boolean compare(String str1, String str2) {
    return Objects.equals(str1, str2);
}
Jobi Subramunian
źródło
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);
}

czy tego właśnie chciałeś?

didxga
źródło
5
Ten przypadek zwraca się, falsejeśli oba ciągi są, nullco może nie być pożądanym rezultatem.
JScoobyCed
1
boolean compare(String str1, String str2) {
    if (str1 == null || str2 == null)
        return str1 == str2;

    return str1.equals(str2);
}
Kierrow
źródło
1
boolean compare(String str1, String str2) {
  return (str1==null || str2==null) ? str1 == str2 : str1.equals(str2);
}
Sumit Singh
źródło
0

OK, więc co oznacza „najlepsze możliwe rozwiązanie”?

Jeśli masz na myśli najbardziej czytelny, wszystkie możliwe rozwiązania są prawie równoważne dla doświadczonego programisty Java. Ale IMO jest najbardziej czytelne

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

Innymi słowy, ukryj implementację w prostej metodzie, którą (najlepiej) można wstawić.

(Możesz również „przełączyć się” na bibliotekę narzędziową innej firmy ... jeśli już używasz jej w swojej bazie kodu.)


Jeśli masz na myśli najbardziej wydajne, to:

  1. najbardziej wydajne rozwiązanie zależy od platformy i kontekstu,
  2. jednym z zagadnień „kontekstowych” jest względna (dynamiczna) częstotliwość występowania nullargumentów,
  3. to chyba nie ma znaczenia, która wersja jest szybsza ... bo różnica jest prawdopodobnie zbyt mała, aby mieć znaczenie dla ogólnej wydajności aplikacji, a
  4. jeśli to ma znaczenie, jedynym sposobem, aby dowiedzieć się, który jest najszybszy NA TWOJEJ PLATFORMIE, jest wypróbowanie obu wersji i zmierzenie różnicy.
Stephen C.
źródło