Google Collections(update: Guava )を使用する場合、コレクション処理コードの簡素化について質問があります。
「コンピューター」オブジェクトがたくさんあるので、それらの「リソースID」のコレクションが必要になります。これは次のように行われます。
_Collection<Computer> matchingComputers = findComputers();
Collection<String> resourceIds =
Lists.newArrayList(Iterables.transform(matchingComputers, new Function<Computer, String>() {
public String apply(Computer from) {
return from.getResourceId();
}
}));
_
さて、getResourceId()
はnullを返すかもしれません(そして、それは今はオプションではありません)が、この場合、結果のStringコレクションからnullを省略したいと思います。
Nullを除外する1つの方法を次に示します。
_Collections2.filter(resourceIds, new Predicate<String>() {
@Override
public boolean apply(String input) {
return input != null;
}
});
_
これをすべて次のようにまとめることができます。
_Collection<String> resourceIds = Collections2.filter(
Lists.newArrayList(Iterables.transform(matchingComputers, new Function<Computer, String>() {
public String apply(Computer from) {
return from.getResourceId();
}
})), new Predicate<String>() {
@Override
public boolean apply(String input) {
return input != null;
}
});
_
しかし、これはエレガントなものではなく、読みやすいのは言うまでもなく、このような簡単なタスクです!実際、単純な古いJavaコード(プレディケートや関数のような凝ったものは一切ありません))は、間違いなくはるかにクリーンです:
_Collection<String> resourceIds = Lists.newArrayList();
for (Computer computer : matchingComputers) {
String resourceId = computer.getResourceId();
if (resourceId != null) {
resourceIds.add(resourceId);
}
}
_
上記を使用することも確かにオプションですが、好奇心から(そしてGoogleコレクションについてもっと学びたいという願望から)、Googleコレクションを使用して短いまたはよりエレガントな方法でまったく同じことを行うことができます?
Predicates
には既にここに役立つ述語があります-Predicates.notNull()
-そしてIterables.filter()
とLists.newArrayList()
は、Iterable
を使用して、これをもう少しクリーンアップできます。
_Collection<String> resourceIds = Lists.newArrayList(
Iterables.filter(
Iterables.transform(matchingComputers, yourFunction),
Predicates.notNull()
)
);
_
実際にCollection
を必要とせず、単にIterable
を必要としない場合、Lists.newArrayList()
呼び出しもなくなる可能性があります。
Function
が再び便利になり、次のように宣言されると最も便利になるでしょう。
_public class Computer {
// ...
public static Function<Computer, String> TO_ID = ...;
}
_
これにより、さらにクリーンアップされます(再利用が促進されます)。
FluentIterable
(グアバ12以降)を使用した「きれいな」構文:
_ImmutableList<String> resourceIds = FluentIterable.from(matchingComputers)
.transform(getResourceId)
.filter(Predicates.notNull())
.toList();
static final Function<Computer, String> getResourceId =
new Function<Computer, String>() {
@Override
public String apply(Computer computer) {
return computer.getResourceId();
}
};
_
返されるリストはImmutableList
であることに注意してください。ただし、copyInto()
メソッドを使用して、要素を任意のコレクションに注ぐことができます。
@ Jon Skeet expected よりも時間がかかりましたが、Java 8ストリームでこれが簡単になります。
_List<String> resourceIds = computers.stream()
.map(Computer::getResourceId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
_
必要に応じて、.filter(x -> x != null)
を使用することもできます。 違いはごくわずかです 。
まず、どこかに定数フィルターを作成します。
public static final Predicate<Object> NULL_FILTER = new Predicate<Object>() {
@Override
public boolean apply(Object input) {
return input != null;
}
}
次に使用できます:
Iterable<String> ids = Iterables.transform(matchingComputers,
new Function<Computer, String>() {
public String apply(Computer from) {
return from.getResourceId();
}
}));
Collection<String> resourceIds = Lists.newArrayList(
Iterables.filter(ids, NULL_FILTER));
コード内のどこでも同じnullフィルターを使用できます。
他の場所で同じコンピューティング関数を使用する場合、それも定数にできます。
Collection<String> resourceIds = Lists.newArrayList(
Iterables.filter(
Iterables.transform(matchingComputers, RESOURCE_ID_PROJECTION),
NULL_FILTER));
確かにC#の同等のものほどニースではありませんが、これはすべてJava 7でクロージャーと拡張メソッドを使用してより良いlot niceを取得します:)
そのように独自のメソッドを書くことができます。これにより、applyメソッドからnullを返すFunctionのnullが除外されます。
public static <F, T> Collection<T> transformAndFilterNulls(List<F> fromList, Function<? super F, ? extends T> function) {
return Collections2.filter(Lists.transform(fromList, function), Predicates.<T>notNull());
}
メソッドは、次のコードで呼び出すことができます。
Collection c = transformAndFilterNulls(Lists.newArrayList("", "SD", "DDF"), new Function<String, Long>() {
@Override
public Long apply(String s) {
return s.isEmpty() ? 20L : null;
}
});
System.err.println(c);