2016-03-10 20 views
7

内の要素を検索します。私はリストを反復し、特定のIDを持つインスタンスを見つけたいと思います。私はストリームを介してそれをやろうとしています。Javaの8ストリームは、私は以下のクラスを持っているリスト

public void foobar() { 

    List<Item> items = getItemList(); 
    List<Integer> ids = getIdsToLookup(); 
    int id, i = ids.size() - 1; 

    while (i >= 0) { 
     id = ids.get(i); 
     Optional<Item> item = items 
      .stream() 
      .filter(a -> a.getId() == id) 
      .findFirst(); 
     // do stuff 
     i--; 
    } 
} 

これはリストを反復処理して必要な要素を取得する最も良い方法ですか?また、lambda式で使用されている変数がfinalまたは実質的にfinalでなければならないというidのフィルタ行にエラーが発生します。おそらく、私はwhileループ内でidを定義することができ、例外を取り除くべきです。ありがとう。ここ

ids.forEach(id -> 
    list.stream() 
    .filter(p -> p.getId() == id) 
    .findFirst() 
    .ifPresent(p -> {//do stuff here}); 
); 

オプションあなたがFindFirstのを呼び出すので、もし、あなたのフィルタ方法は、それが1つのまたはゼロの要素を見つけることができ、空のストリームを返すことができることを示しています

+1

リストのインデックスとラムダの現在のアイテムに同じ変数iを使用しています。別の名前を選択してください。そのコードは問題ありませんが、非常に非効率です。複数のidsがあり、すべてのアイテムを見つける必要がある場合は、まずHashMap を作成してから、HashMapを使用します。 –

+0

私はコード内で別の変数を使用していますが、私はここでコードを単純化しようとしていました。私はそれを変更します。 –

+1

ループ内で変数 'id' **を宣言すると、それは実質的に最終的になります。外に出ることで、各反復でそれを再初期化します。したがって、最終的なものではありません。可能な限り小さな範囲で変数を宣言することが一般的にベストプラクティスです。 –

答えて

5

あなたが検索するIDの多くを持っている場合、むしろ、各IDの線形検索を行うよりも、単一のパスでそれをしないソリューションを使用することをお勧めします:

Map<Integer,Optional<Item>> map=ids.stream() 
    .collect(Collectors.toMap(id -> id, id -> Optional.empty())); 
items.forEach(item -> 
    map.computeIfPresent(item.getId(), (i,o)->o.isPresent()? o: Optional.of(item))); 
for(ListIterator<Integer> it=ids.listIterator(ids.size()); it.hasPrevious();) { 
    map.get(it.previous()).ifPresent(item -> { 
     // do stuff 
    }); 
} 

最初の文だけでマップを作成idsリストから各検索IDを空のOptionalにマッピングします。

forEachを使用してアイテムの上に第二の文を反復し、各項目について、それはすべて、そこにそのIDからのマッピングが空Optionalにだと、このようなマッピングがある場合、アイテムを封入Optionalと交換するかどうかをチェックします1回の操作で、computeIfPresent

最後のforループは、idsリストを逆順に反復します。その順番で処理したい場合は、Optionalが空であればアクションを実行します。マップがリスト内のすべてのIDで初期化されているため、getnullを返すことはなく、IDがitemsリストにない場合は空のOptionalを返します。Mapの検索が一般的な実装の場合であるO(1)時間の複雑さを、持っていると仮定して、ネットの時間複雑性はO(m+n)O(m×n)から変更の方法、

...

+0

私は、1回のパスを行い、必要なすべてのエントリを取得するというアイデアが気に入っています。しかし、私はidsリストで受け取った順番でエントリを処理する必要があります。私は上記のコードが注文に気をつけているとは思わない、そうですか?また、上記のコードで何をしようとしているのかについていくつか説明することができますか?それは助けになるでしょう。ありがとう。 –

+0

@Gengis Khan:それはあなたが望むものとまったく同じです。 'for'ループは、あなたが望むように' ids'リストの上を逆向きに繰り返します。 – Holger

8

あなたは、このような使用の何かを試すことができます。

+0

'findFirst()'を 'findAny()'に置き換えると、ここでのパフォーマンスが向上します。 https://stackoverflow.com/questions/35359112/difference-between-findany-and-findfirstfirst-in-java-8 – stuart

2

あなたがストリームに固執し、逆方向反復処理したい場合、あなたはこのようにそれを行うことができ:

IntStream.iterate(ids.size() - 1, i -> i - 1) 
    .limit(ids.size()) 
    .map(ids::get) // or .map(i -> ids.get(i)) 
    .forEach(id -> items.stream() 
     .filter(item -> item.getId() == id) 
     .findFirst().ifPresent(item -> { 
      // do stuff 
     })); 

このコードは、あなたと同じように行います。

これは、シード:ids.size() - 1から始まり、後方に反復します。 intの初期ストリームは、limit()でサイズが制限されているため、負の値はintであり、ストリームはidsのリストと同じサイズです。次に、map()オペレーションは、インデックスをidsリストのi番目の位置にある実際のidに変換します(これはids.get(i)を呼び出すことによって行われます)。最後に、項目はコード内と同じ方法でitemsリスト内で検索されます。

+2

ストリーム操作は、とにかく 'O(n)'複雑さを意味します... – Holger

+0

@Holger私は 'ids 'list –

+1

@Holger今、私はあなたが何を意味するかを見て、そのメモを削除するために編集するでしょう。ありがとう! –

0

あなたがのために最大1つのアイテムを見つけたいですそれぞれidを与えられ、見つけられた項目で何かをしますか?パフォーマンスの改善:

Set<Integer> idsToLookup = new HashSet<>(getIdsToLookup()); // replace list with Set 
items.stream().filter(e -> idsToLookup.remove(e.getId())).forEach(/* doing something */);