2016-09-12 15 views
2

私は配列にする必要があります:arrAarrBarrAおよびarrBは、異なるタイプのオブジェクトのリストであり、add関数は、オブジェクトAをオブジェクトBに変換します。 arrAからarrBまでの各オブジェクトを追加し、そのオブジェクトをarrAから削除したいと思います。イムは、ストリームでこれをやろうとしている:私はこれを実行するとforeachメソッドでストリームからオブジェクトを削除する方法は?

arrA.stream().foreach(c -> {arrB.add(c); arrA.remove(c);}); 

、二つのことが起こっている:

  1. すべてのオブジェクトがARRAからarrBに渡されません。
  2. 数回の反復の後に、NULLポインタ例外がスローされます。

iは、アレイをコピーすることによって、私はこれを解決する可能性が今

arrBに渡され、奇数インデックスの下でのみオブジェクト)配列の長さはそれぞれremove()呼び出し後に減少され、反復カウンタが増加されるので、それはですgues 1つのストリームコールをしてから2番目のストリームコールでオブジェクトを削除しますが、これは私のために正しいようです。

この問題を解決するにはどうすればよいですか?

EDIT。 追加情報: 実際の実装では、以前に切り抜いたリスト(arrC, arrDなど)に切り抜いた条件を満たす要素を追加する

arrA.stream().filter(some condition).foreach(c -> {arrB.add(c); arrA.remove(c);}); 

とそのと呼ばれる数回濾過が、各オブジェクトは1つのリストだけ

を上に置くことができた場合、このリスト
+2

オブジェクトを1つずつ追加したり削除したりする必要がありますか? 1つのメソッド呼び出しでリスト全体をクローンし、もう一方をクリアすることはできますか? –

+0

とにかく 'arrA.remove'のポイントは何ですか?それは空で終わるつもりです、それは不変です。 –

+0

が追加されました。簡潔に説明して編集しました。なぜ要素を1つずつ削除する必要がありますか? –

答えて

9

ストリームは、より機能的な方法で使用するように設計されており、好ましくはコレクションを不変として扱うように設計されています。

非ストリームの方法は、次のようになります。

arrB.addAll(arrA); 
arrA.clear(); 

あなたは、Streamsを使用している場合がありますしかし、それはより多くのようですので、あなたが入力をフィルタリングすることができるように:

arrB.addAll(arrA.stream().filter(x -> whatever).toList()) 

その後、(ARRAからのおかげを削除コメントのための@Holgar)。

arrA.removeIf(x -> whatever) 

あなたの述語が高価である場合、あなたはできるパーティション:

Map<Boolean, XXX> lists = arrA.stream() 
    .collect(Collectors.partitioningBy(x -> whatever)); 
arrA = lists.get(false); 
arrB = lists.get(true); 

または変更のリストを作る:

List<XXX> toMove = arrA.stream().filter(x->whatever).toList(); 
arrA.removeAll(toMove); 
arrB.addAll(toMove); 
+0

+1です。これは、OPが望むものに最も近いと思われる。他のすべては、OPが望んでいる、あるいはそれほど効率が悪いと思われるものを直接実行していないようです。 –

+1

@Martin Nyolt:実際には、 "変更のリスト"アプローチよりも悪いことはありません。 'removeAll'の時間複雑さは、すべての要素がフィルタと一致する場合、2次までn×mになります。 – Holger

+1

'partitioningBy'アプローチは私の+1に値する。補題として、 'arrA'が変更可能であれば(初期の' remove'アプローチが示唆しているように)、 'arrA = arrA.stream()。filter(x-> whatever).toList()'は 'arrA.removeIf (x - >!何でも); ... ... – Holger

0

あなたはそれを反復している間にarrAから削除することはできないと思います。

これを回避するには、新しいArrayListにラップしてください。<>();

new ArrayList <>(arrA).stream()。foreach(c - > {arrB.add(c); arrA.remove(c);});

+1

"ラッピング"は実際には 'arrA'のクローンを作成します。 –

1

iは、配列の長さはそれぞれのremove()の呼び出しの後に減少し、反復カウンタが

を高めているからだと思います。 for-each-loopは普通のfor-loopと似ていますが、書き込みと読み込みが簡単です。あなたはそれを統語的な砂糖と考えることができます。内部的には、イテレータまたは配列インデックスを使用します。 forEachストリームの方法は、パラレル実行と機能的なコーディングスタイルを可能にするよりファンシーなバージョンです。 but has its own drawbacks

インデックス付きループと同様に、ループ中に要素を削除すると、ループが中断されます。最初の反復で要素0を削除すると、リスト項目は1つ上にシフトし、次の反復には要素0(以前は1)と1(以前は2)があります。 。ループ変数は1を指しているので、実際に次の項目をスキップします。インデックス2になると、作業中のループには1つのアイテムしか残っていません(2つを削除しました)。インデックスが範囲外であるため、エラーが発生します。

考えられる解決策:

  • クローニングおよびクリアリストのListメソッドを使用します。
  • 実際に各アイテムのメソッドを呼び出す必要がある場合は、2つのループで行います。
+1

あなたは拡張されたforループについて話しています。ストリームの 'forEach'メソッドについてではありません。 – RealSkeptic

+0

それについてのメモを加えた。最後の "変更のリスト"は –

3

他の人が述べたように、これは可能ではありませんforeachと - 要素を削除するためにfor (A a: arrA)ループでは不可能です。

私の意見では、もっともクリーンな解決策は、イテレータを使用してwhileにプレーンを使用することです。イテレータは反復中に(コレクションがサポートしている限り)要素を削除できます。

Iterator<A> it = arrA.iterator() 
while (it.hasNext()) { 
    A a = it.next(); 
    if (!check(a)) 
     continue; 
    arrB.add(a); 
    it.remove(); 
} 

これにより、コピー/クローニングarrAが保存されなくなります。

+1

私はこの解決策を好む。私は、私が必要とするものを達成するために彼らと戦う必要がない限り、川が良いと信じています。 – RealSkeptic

-1

あなただけでもCollections.addAllとすることができます。それが終わったら。 arrAでclear()と電話するだけです。

+0

OPsの更新を参照してください。これは彼が望むものではありません(選択した要素のみを削除します)。 –

関連する問題