web-dev-qa-db-ja.com

Guavaを使用してコレクションを変換するときにnullを削除するエレガントな方法はありますか?

Google Collections(updateGuava )を使用する場合、コレクション処理コードの簡素化について質問があります。

「コンピューター」オブジェクトがたくさんあるので、それらの「リソース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コレクションを使用して短いまたはよりエレガントな方法でまったく同じことを行うことができます

59
Jonik

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 = ...;
}
_

これにより、さらにクリーンアップされます(再利用が促進されます)。

79
Cowan

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()メソッドを使用して、要素を任意のコレクションに注ぐことができます。

35
Natix

@ Jon Skeet expected よりも時間がかかりましたが、Java 8ストリームでこれが簡単になります。

_List<String> resourceIds = computers.stream()
    .map(Computer::getResourceId)
    .filter(Objects::nonNull)
    .collect(Collectors.toList());
_

必要に応じて、.filter(x -> x != null)を使用することもできます。 違いはごくわずかです

14
Jeffrey Bosboom

まず、どこかに定数フィルターを作成します。

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を取得します:)

5
Jon Skeet

そのように独自のメソッドを書くことができます。これにより、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);
1
BewdyM8