web-dev-qa-db-ja.com

Javaで3つ以上の文字列の等価性をチェックする

これは非常に基本的な質問であることはわかっていますが、コードをできるだけ簡潔かつ簡潔にする方法を常に模索しています。単一のif()ステートメントで3つ以上の文字列の同等性を比較する方法はありますか?現在、_&&_演算子を使用して、各文字列を段階的に比較しています。ただし、ご想像のとおり、長い変数名と呼び出されるメソッドの間では、これらの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();
}
_
12
Robert Krupp

このようにequals()コマンドをチェーンする簡単な方法はありません。これらをこのようにチェーンできるようにするために、equals()は文字列自体への参照を返す必要があります。ただし、比較の結果を表すブール値を返すことはできません。

また、最初の例のように3つの文字列を個別に比較するために特に問題はありませんと見なします。コードが適切にフォーマットされていることを確認してください。長い変数名でも、簡単に理解できます。

_if(myValueAWithALongName.equals(myValueBWithALongName) &&
   myValueBWithALongName.equals(myValueCWithALongName) &&
   myValueCWithALongName.equals(myValueDWithALongName) &&
   ... ) {
_

または、このスレッドで提案されている他のソリューションの1つを使用して、値をコレクションにラップし、ヘルパーメソッドを作成することもできます。ただし、これはメモリの使用量、パフォーマンス、場合によっては読み取り可能性に悪影響を及ぼす可能性があることに注意してください。


余談ですが、コンストラクタnew String()を使用して文字列を作成しないでください。文字列リテラルを変数に直接割り当てるだけです。

_String a = "a";
_
16
Timo

同じタイプのオブジェクトが複数ある場合は、配列やリストなどのデータ構造に整理することをお勧めします。したがって、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
}
14
Tagir Valeev

比較されるStringオブジェクトの数に応じて、この問題に対処するさまざまな方法があります。

比較するStringオブジェクトがいくつかあり、アプリケーションの1か所または2か所でこの種の比較を行う必要がほとんどある場合は、そのような必要なクラス内のメソッドでコードをラップできます。コードを読みやすくするための比較:

 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.equalsNullPointerExcsptionからあなたを守ります。私の意見では、これは絶対に必要です。

この種の比較が非常に頻繁に必要な場合(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) { }
3
CKing

これは珍しいアイデアかもしれませんが、このような状況では、少しのメタプログラミングが役立つ場合があります。

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

このような単純なコード生成の場合、エラーのマージンはわずかです。唯一の欠点は保守性です。誰かがそれをいじくる必要がある場合(たとえば、nullの処理を変更し、これは私とあなたの例の両方で無視されます)、彼らはジェネレーターを変更して再度実行する必要があるためです。

2
Radiodef

さらに、これらの文字列の数を不明にする予定であり、それらの中にネストされたif()が入れ子になっている複雑なforループを回避したいと思います。

Forループがそれほど複雑になるとは思いません。

あなたは確かに試すことによって同等性をチェックすることができます

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

ただし、すべてをaと比較しても同じ結果になります。すべてがaと等しい場合、すべてが互いに等しくなります。

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

これにより、forループが単純になります。 aをそれ自体と比較するオーバーヘッドを気にしない場合は、配列全体またはコレクション(またはその他)をと比較して、等しいかどうかを確認できます。

Ifも乱雑に見えません。 returnは、1組の文字列が等しくなくなるとすぐに関数から取得されます。

2
null

このようなものはどうですか:

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

正確ではありませんが、一度書くだけで、任意の数の文字列で機能します。

1
niceguy