特定の値を持つフィールドを持つオブジェクトがList
に含まれているかどうかをチェックしたいです。これで、ループを使用して確認してみることができましたが、もっとコード効率のよいものがあるかどうかに興味がありました。
何かのようなもの;
if(list.contains(new Object().setName("John"))){
//Do some stuff
}
私は上記のコードが何もしないことを知っています、それは私が達成しようとしていることを大まかに示すことだけです。
また、明確にするために、単純なループを使用したくないのは、このコードは現在、ループの内側にあるループの内側にあるループの内側に入るためです。読みやすくするために、これらのループにループを追加し続けたくはありません。それで、私は単純な(ish)代替案があるかどうか疑問に思いました。
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 -> {
//...
}
);
}
o
はMyObject
インスタンスを表します。
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);
ただし、次の場合は、これが選択肢にならない可能性があります。
これらのどちらかが当てはまる場合は、オプション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;
}
残念ながらそれを回避するより良い方法はありません。
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 フレームワークは同様のものを提供します。
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の多数の欠点と、他の言語がどのように優れているかについての多くの例が含まれています。
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
を返します。これを使用すると、さまざまな検索方法でオブジェクトを検索できます。
これは、Java 8以降を使用してそれを行う方法です。
boolean johnExistance = list.stream().anyMatch(o -> o.getName().equals("John"));
値の1つをキーとして使用してHashmap<String, Object>
を作成し、yourHashMap.keySet().contains(yourValue)
がtrueを返すかどうかを確認できます。
Eclipse Collections を使用している場合は、anySatisfy()
メソッドを使用できます。可能であれば、List
をListAdapter
に変更するか、List
をListIterable
に変更してください。
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");
List
をListIterable
に変更できない場合は、ListAdapter
を使用する方法は次のとおりです。
boolean result =
ListAdapter.adapt(list).anySatisfyWith(MyObject::named, "John");
注:私はEclipseの選択のコミッターです。
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());
}
});
contains
メソッドは内部的にequals
を使用します。だからあなたはあなたの必要に応じてあなたのクラスのequals
メソッドをオーバーライドする必要があります。
ところでこれは文法的に正しいように見えません:
new Object().setName("John")
これは 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);
}
});
}
この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);
と同じ結果になります。
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 );