web-dev-qa-db-ja.com

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

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

何かのようなもの;

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

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

また、明確にするために、単純なループを使用したくないのは、このコードは現在、ループの内側にあるループの内側にあるループの内側に入るためです。読みやすくするために、これらのループにループを追加し続けたくはありません。それで、私は単純な(ish)代替案があるかどうか疑問に思いました。

149
Rudi Kershaw

ストリーム

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

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

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

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

185
Josh M

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

1.最初の選択は望ましいことですが、Objectクラスの `equals()`メソッドをオーバーライドすることです。

たとえば、このObjectクラスがあるとしましょう。

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

MyObjectの名前だけを気にするとしましょう。2つの `MyObject`が同じ名前を持つ場合、それらは等しいと見なされるべきです。その場合、名前を比較して等しいかどうかを判断するように、 `equals()`メソッド(そして `hashcode()`メソッドも)をオーバーライドしたいでしょう。

これが完了したら、次のようにして、Collectionに "foo"という名前のMyObjectが含まれているかどうかを確認できます。

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

ただし、次の場合は、これが選択肢にならない可能性があります。

  • 等価性をチェックするために名前と位置の両方を使用していますが、Collectionに特定の位置を持つ `MyObject`があるかどうかだけを確認したいのです。この場合、すでにequals()をオーバーライドしています。
  • `MyObject`はあなたが変更する自由がないAPIの一部です。

これらのどちらかが当てはまる場合は、オプション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;
    }

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

75
James Dunn

Google Guava

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 フレームワークは同様のものを提供します。

24
Brian Agnew

Collection.contains()は、trueが返されるまで各オブジェクトに対してequals()を呼び出すことによって実装されます。

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

したがって、 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の多数の欠点と、他の言語がどのように優れているかについての多くの例が含まれています。

18
Aaron Digulla

バイナリ検索

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を返します。これを使用すると、さまざまな検索方法でオブジェクトを検索できます。

18
Debojit Saikia

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

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

地図

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

8
user7110558

Eclipseのコレクション

Eclipse Collections を使用している場合は、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の選択のコミッターです。

6
Craig P. Motlin

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クラスにそれの実装を渡しています。

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

あなたがそれを使用したいのであれば、あなたがする必要があるのはメソッドを呼び出すとタイプ述語を定義することだけです

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

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

ところでこれは文法的に正しいように見えません:

new Object().setName("John")
3
Juned Ahsan

これは Guava を使った解決策です。

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
Phan Van Linh

この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
Magda

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 );
0
Pasha Gharibi