2009-11-26 3 views
59

Googleコレクション(アップデートGuava)を使用しているときに、コレクション処理コードの簡素化について質問があります。Guavaを使用してコレクションを変換する際に、nullを削除するエレガントな方法はありますか?

「コンピュータ」オブジェクトがたくさんあり、「リソース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コレクションからヌルを省略したいと思います:これはそうのように行われます。あなたはこのように一緒にすべてのことを置くことができ

Collections2.filter(resourceIds, new Predicate<String>() { 
    @Override 
    public boolean apply(String input) { 
     return input != null; 
    } 
}); 

は、ここで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コレクションのを使用すると、より短くて洗練された方法で同じことをすることができますか?

答えて

76

あり、ここであなたを助けるPredicatesで述語すでにだ - Predicates.notNull()は - あなたはIterables.filter()Lists.newArrayList()はへIterableを取ることができるという事実を使用することができますクリーンこれはもう少しです。

Collection<String> resourceIds = Lists.newArrayList(
    Iterables.filter(
    Iterables.transform(matchingComputers, yourFunction), 
    Predicates.notNull() 
) 
); 

あなたが実際にちょうどIterableCollectionを必要としない場合は、Lists.newArrayList()呼び出しがあまりにも離れて行くことができますし、クリーナー、再び一歩です!

私はあなたがFunctionが再び便利になるだろう、さらにこれをクリーンアップ(と再利用を推進していきます)

public class Computer { 
    // ... 
    public static Function<Computer, String> TO_ID = ...; 
} 

として宣言された最も有用であろうことを見つけるかもしれないと思います。

+1

ニース - なぜ私はその前にPredicatesメソッドを見つけたことがないのか分かりません... –

+0

優れたアドバイス、ありがとうございます! Predicates.notNull()を使用して関数を定数に入れると、実際にはコードがかなり明確になります。 – Jonik

+3

素晴らしい:)。私は変換としてフォントを使用するとき、私は静的メソッドでそれを分離し、それをXXX()に命名するのが好きです。読みやすいと思います。この場合、次のようになります。transform(matchingCompters、intoResourceId())。 –

5

まず、私はどこかに一定のフィルタを作成したい:

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

あなたは、あなたのコード内のどこでも同じヌルフィルタを使用することができます。

あなたが他の場所で同じコンピューティング機能を使用する場合は、あなただけ残して、あまりにも一定のことを行うことができます:

Collection<String> resourceIds = Lists.newArrayList(
    Iterables.filter(
     Iterables.transform(matchingComputers, RESOURCE_ID_PROJECTION), 
     NULL_FILTER)); 

をそれは確かにC#の同等のようになりほど素敵ではないのですが、これはすべて取得する予定ですたくさんの閉鎖や拡張メソッドを持つJava 7でよりよい :)

+5

私はそれをNOT_NULL_FILTERと個人的に呼んでいます。 :)そして、既にPredicatesクラスの静的メソッドがあります(私の答えを見てください)。 – Cowan

+1

@Cowan:それはあなたが "フィルタ"をどのように扱うかによって異なります - あなたはそれがヌルをフィルタリングすると主張できます。それは命名の前面に一般的な痛みです。しかし、逆のことが愚かなので、それがどうなるのかははっきりしていると思う:) Predicatesメソッドの呼び出しがうまくいく。 –

1

このように独自の方法を書くことができます。これは、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()); 
    } 

このメソッドは、次のコードで呼び出すことができます。

33
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); 

FluentIterable(グアバ12以降)とビット "きれい" 構文:返されたリストはImmutableListあること

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

注意。ただし、copyInto()メソッドを使用すると、要素を任意のコレクションに挿入できます。

13

それは長い@Jon Skeet expected以上かかりましたが、Java 8のストリームが、これはシンプルに実行します。

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

あなたが好きな場合にも.filter(x -> x != null)を使用することができます。 the difference is very minor

+0

私はguavaの 'Predicates#notNull()'に相当するJava8を探していました。 'Objects'を見たことはありません –

+0

ここにAPIドキュメントがあります:https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#nonNull-java.lang.Object- – amoebe

関連する問題