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

StackOverflow https://stackoverflow.com/questions/1802629

質問

Googleコレクションを使用する際に、コレクション処理コードの簡素化について質問があります(更新グアバ)。

「コンピュータ」がたくさんあります。オブジェクト、および「リソース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コード(派手なPredicateやFunctionを一切使用しない)は、間違いなくはるかにクリーンです:

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の予想よりも時間がかかりましたが、Java 8ストリームによりこれが簡単になります。

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

必要に応じて .filter(x-&gt; 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でより良い多くを得るでしょう:)

次のように独自のメソッドを作成できます。これにより、applyメソッドからnullを返す関数の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);
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top