Java List.contains(フィールド値がxに等しいオブジェクト)


199

List特定の値のフィールドを持つオブジェクトがに含まれているかどうかを確認したいと思います。これで、ループを使用してチェックを行うことができましたが、もっと効率的なコードがあるかどうか知りたいと思いました。

何かのようなもの;

if(list.contains(new Object().setName("John"))){
    //Do some stuff
}

上記のコードは何も実行しないことを知っています。これは、私が達成しようとしていることを大まかに示すためです。

また、明確にするために、単純なループを使用したくない理由は、このコードは現在、ループ内のループ内にあるループ内に移動するためです。読みやすくするために、これらのループにループを追加し続けたくありません。だから私は、何かシンプルな方法があるのか​​と思いました。


5
これはカスタムの等価であるため、カスタムコードを記述する必要があります。
Sotirios Delimanolis 2013

指定された目標とコード例が一致していないようです。1つのフィールド値のみに基づいてオブジェクトを比較しますか?
ダンカンジョーンズ

1
equals(Object)カスタムオブジェクトのメソッドをオーバーライドしますか?
Josh M

1
for(Person p:list) if (p.getName().equals("John") return true; return false;残念ながら、Javaでもっと簡潔な方法を見つけることはできません。
MikeFHay 2013

@Rajdeep申し訳ありませんが、あなたの質問は理解できません。常に真であるp.equals(p) べきなので、私はあなたが達成しようとしていることを混乱させています。うまくいけば、新しい質問をすると、より良い支援を受けることができます。
MikeFHay 2015

回答:


267

ストリーム

Java 8を使用している場合、おそらく次のようなことを試すことができます。

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().filter(o -> o.getName().equals(name)).findFirst().isPresent();
}

または、次のようなことを試すこともできます。

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().map(MyObject::getName).filter(name::equals).findFirst().isPresent();
}

このメソッドは、にという名前のが含まれているtrue場合に戻ります。thatのそれぞれに対して操作を実行したい場合は、次のようなことを試すことができます。List<MyObject>MyObjectnameMyObjectgetName().equals(name)

public void perform(final List<MyObject> list, final String name){
    list.stream().filter(o -> o.getName().equals(name)).forEach(
            o -> {
                //...
            }
    );
}

Where oMyObjectインスタンスを表します。

または、コメントが示唆するように(MK10に感謝)、次のStream#anyMatchメソッドを使用できます。

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().anyMatch(o -> o.getName().equals(name));
}

1
次に、2番目の例はになりますpublic boolean。また、あなたが使用する場合がありますObjects.equals()場合にはo.getName()可能null
Eric Jablow 2013

1
私は妄想的なプログラマーです。私は、人々がヌルで速くゆるいプロジェクトを扱ってきたので、防御的な傾向があります。アイテムがnullにならないようにするためのはるかに良い方法です。
Eric Jablow 2013

5
@EricJablowたぶん、1つだけ(私のもの)ではなく、チェックを実行しないすべての回答にコメントする必要がありますnull
Josh M

55
さらに短くてわかりやすく: return list.stream().anyMatch(o -> o.getName().equals(name));
MK10

3
@ MK10には同意しますがjava.util.Objects、nullsafe比較に使用return list.stream().anyMatch(o -> Objects.euqals(o.getName(), name);しますnullのストリーム内のオブジェクトを確認する場合は、.filter(Objects::nonNull)anyMatch の前にを追加できます。
tobijdc 2016

81

2つの選択肢があります。

1.推奨される最初の選択は、Objectクラスの `equals()`メソッドをオーバーライドすることです。

たとえば、次のObjectクラスがあるとします。

public class MyObject {
    private String name;
    private String location;
    //getters and setters
}

ここで、MyObjectの名前のみが重要で、一意である必要があるとしましょう。2つの `MyObject`が同じ名前である場合、それらは等しいと見なされます。その場合、 `equals()`メソッド(および `hashcode()`メソッドも)をオーバーライドして、名前を比較して等しいかどうかを判断します。

これを実行したら、次のようにして、コレクションに「foo」という名前のMyObjectが含まれているかどうかを確認できます。

MyObject object = new MyObject();
object.setName("foo");
collection.contains(object);

ただし、次の場合、これはオプションではない可能性があります。

  • 名前と場所の両方を使用して等しいかどうかを確認していますが、コレクションに特定の場所の `MyObject`があるかどうかだけを確認したいとします。この場合、 `equals()`はすでにオーバーライドされています。
  • `MyObject`は、変更する自由がないAPIの一部です。

これらのいずれかに該当する場合は、オプション2が必要です。

2.独自のユーティリティメソッドを記述します。

public static boolean containsLocation(Collection<MyObject> c, String location) {
    for(MyObject o : c) {
        if(o != null && o.getLocation.equals(location)) {
            return true;
        }
    }
    return false;
}

または、ArrayList(またはその他のコレクション)を拡張して、それに独自のメソッドを追加することもできます。

public boolean containsLocation(String location) {
    for(MyObject o : this) {
        if(o != null && o.getLocation.equals(location)) {
                return true;
            }
        }
        return false;
    }

残念ながらそれを回避するより良い方法はありません。


if(o!= null && o.getLocation.equals(location))でゲッターのブラケットを忘れたと思います
olszi

25

Googleグアバ

Guavaを使用している場合は、機能的なアプローチを使用して次のことを実行できます

FluentIterable.from(list).find(new Predicate<MyObject>() {
   public boolean apply(MyObject input) {
      return "John".equals(input.getName());
   }
}).Any();

少し冗長に見えます。ただし、述語はオブジェクトであり、検索ごとに異なるバリアントを提供できます。ライブラリ自体が、コレクションの反復と適用する関数をどのように分離するかに注意してください。equals()特定の動作のためにオーバーライドする必要はありません。

以下に示すように、Java 8以降に組み込まれているjava.util.Streamフレームワークは、同様の機能を提供します。


1
またはIterables.any(list, predicate)、実質的に同じを使用することもできます。
MikeFHay 2013

@EricJablowうん。:)あなたは私の解決策を見ることができます。
Josh M

25

これは、Java 8以降を使用して行う方法です。

boolean isJohnAlive = list.stream().anyMatch(o -> o.getName().equals("John"));

20

Collection.contains()equals()戻るまで各オブジェクトを呼び出すことで実装されますtrue

したがって、これを実装する1つの方法はオーバーライドequals()することですが、もちろん、1つのイコールしか持てません。

したがって、Guavaのようなフレームワークはこれに述語を使用します。を使用Iterables.find(list, predicate)すると、述語にテストを置くことにより、任意のフィールドを検索できます。

VMの上に構築された他の言語には、これが組み込まれています。たとえば、Groovyでは、次のように記述するだけです。

def result = list.find{ it.name == 'John' }

Java 8によって、私たちの生活もすべて簡単になりました。

List<Foo> result = list.stream()
    .filter(it -> "John".equals(it.getName())
    .collect(Collectors.toList());

このようなことに関心があるなら、「Beyond Java」という本をお勧めします。これには、Javaの数多くの欠点と他の言語がどのように優れているかを示す多くの例が含まれています。


したがって、リストに追加されるカスタムオブジェクトのequalsメソッドをオーバーライドすると、特定のクラス変数が同じである場合にtrueを返すようにできますか?
Rudi Kershaw 2013

1
いいえ、背後にあるアイデアequals()は非常に限られています。これは、「オブジェクトの同一性」をチェックすることを意味します-オブジェクトのあるクラスにとって意味があるかもしれないことは何でも。どのフィールドを含める必要があるかを示すフラグを追加すると、さまざまな問題が発生する可能性があります。私はそれに反対することを強く勧めます。
アーロンディグラ2013

Groovyが大好きなので、「新しい」Java 8 Lambdaはまだこのクールさをまだ与えてくれませんか? def result = list.find{ it.name == 'John' }Oracle / JCPは、プログラマーに対する戦争犯罪で訴えられるべきです。
2016

19

バイナリ検索

Collections.binarySearchを使用して、リスト内の要素を検索できます(リストがソートされている場合)。

Collections.binarySearch(list, new YourObject("a1", "b",
                "c"), new Comparator<YourObject>() {

            @Override
            public int compare(YourObject o1, YourObject o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });

オブジェクトがコレクションに存在しない場合は負の数を返し、そうでない場合はオブジェクトのを返しindexます。これにより、さまざまな検索方法でオブジェクトを検索できます。


これは、グアバを使用したくない小規模なプロジェクトに最適なソリューションです。ただし、binarySearchのドキュメントには、「この呼び出しを行う前に、指定されたコンパレータに従ってリストを昇順で並べ替える必要があります。並べ替えられていない場合、結果は未定義です」と記載されています。しかし、場合によっては、比較対象に応じて、そうではない可能性があります。
chrismarx 2014

負の数は、オブジェクトを追加して再ソートした場合にオブジェクトが挿入される場所を表します。
fiorentinoing 2016年

8

地図

Hashmap<String, Object>キーとして値の1つを使用してを作成し、yourHashMap.keySet().contains(yourValue)trueが返されるかどうかを確認できます。


+1これは、最初に詳細をマップに配置する際に少しオーバーヘッドが発生することを意味しますが、その後、一定時間のルックアップを取得します。これまで誰もこれを提案していないことに驚いています。
Rudi Kershaw 2017

6

Eclipseコレクション

あなたが使用している場合はEclipseのコレクションを、あなたが使用することができるanySatisfy()方法を。どちらかあなたを適応させるListListAdapterしたりを変更するListListIterable可能であれば。

ListIterable<MyObject> list = ...;

boolean result =
    list.anySatisfy(myObject -> myObject.getName().equals("John"));

このような操作を頻繁に行う場合は、型に属性があるかどうかを答えるメソッドを抽出することをお勧めします。

public class MyObject
{
    private final String name;

    public MyObject(String name)
    {
        this.name = name;
    }

    public boolean named(String name)
    {
        return Objects.equals(this.name, name);
    }
}

anySatisfyWith()メソッド参照とともに代替フォームを使用できます。

boolean result = list.anySatisfyWith(MyObject::named, "John");

をに変更できない場合ListListIterable、次のように使用しますListAdapter

boolean result = 
    ListAdapter.adapt(list).anySatisfyWith(MyObject::named, "John");

注:私はEclipseコレクションのコミッターです。


5

Predicate

Java 8、またはコレクションを処理するためのより多くの機能を提供するライブラリを使用しない場合は、ソリューションよりも再利用可能なものを実装できます。

interface Predicate<T>{
        boolean contains(T item);
    }

    static class CollectionUtil{

        public static <T> T find(final Collection<T> collection,final  Predicate<T> predicate){
            for (T item : collection){
                if (predicate.contains(item)){
                    return item;
                }
            }
            return null;
        }
    // and many more methods to deal with collection    
    }

私はそのようなものを使用しています、私は述語インターフェースを持っています、そしてそれを私のutilクラスに実装を渡しています。

私のやり方でこれを行うことの利点は何ですか?タイプコレクションでの検索を処理する1つのメソッドがあります。別のフィールドで検索する場合は、別個のメソッドを作成する必要はありません。あなたがする必要があるすべてのことは、それがもはや役に立たなくなったらすぐに破壊することができる異なる述語を提供することです/

あなたがそれを使いたいなら、あなたがする必要があるのはメソッドを呼んであなたの述語を定義することだけです

CollectionUtil.find(list, new Predicate<MyObject>{
    public boolean contains(T item){
        return "John".equals(item.getName());
     }
});

4

ここにグアバを使用したソリューションがあります

private boolean checkUserListContainName(List<User> userList, final String targetName){

    return FluentIterable.from(userList).anyMatch(new Predicate<User>() {
        @Override
        public boolean apply(@Nullable User input) {
            return input.getName().equals(targetName);
        }
    });
}

3

containsメソッドはequals内部的に使用します。したがって、必要に応じequalsてクラスのメソッドをオーバーライドする必要があります。

ところで、これは構文的に正しく見えません:

new Object().setName("John")

3
セッターが戻らない限りthis
Sotirios Delimanolis 2013

したがって、リストに追加されるカスタムオブジェクトのequalsメソッドをオーバーライドすると、特定のクラス変数が同じである場合にtrueを返すようにできますか?
Rudi Kershaw 2013

@RudiKershawはい、そうです。1つ、2つ、またはすべてのフィールドに基づいてtrueを返すことができます。したがって、同じ名前の2つのオブジェクトが等しいオブジェクトであると言うことが論理的に正しいと思われる場合は、名前を共マッピングするだけでtrueを返します。
Juned Ahsan 2013

1
equals一般的にクラスに意味がない場合を除いて、単一のユースケースでオーバーライドすることはお勧めできません。ループを書くだけの方がはるかに良いでしょう。
MikeFHay 2013

@MikeFHayが同意したので、「同じ名前の2つのオブジェクトは等しいオブジェクトであると言うのが論理的に正しいと思われる場合、trueを返すと論理的に正しい」
Juned Ahsan

1

これをList.contains(Object with field value equal to x)繰り返し実行する必要がある場合、簡単で効率的な回避策は次のとおりです。

List<field obj type> fieldOfInterestValues = new ArrayList<field obj type>;
for(Object obj : List) {
    fieldOfInterestValues.add(obj.getFieldOfInterest());
}

次に、List.contains(Object with field value equal to x)同じ結果になりますfieldOfInterestValues.contains(x);


1

次のものもanyMatch()使用できます。

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().anyMatch(o -> o.getName().contains(name));
}

0

JAVA 8 SDKにもかかわらず、たとえばhttp://commons.apache.org/proper/commons-collections/など、ライブラリを使用して作業するのに役立つ多くの収集ツールがあります。

Predicate condition = new Predicate() {
   boolean evaluate(Object obj) {
        return ((Sample)obj).myField.equals("myVal");
   }
};
List result = CollectionUtils.select( list, condition );
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.