2016-03-10 9 views
5

間隔の一覧(開始順)があり、それらを分割して、間隔の重複グループの一覧があるとします。したがって、たとえば、などIntervalで:Java 8パーティションは、前の要素を含む条件でグループ化して一覧表示します。

public class Interval { 
    private final int start; 
    private final int end; 

    public Interval(int start,int end){ 
     this.start = start; 
     this.end = end; 
    } 

    public int getStart(){return start;} 
    public int getEnd(){return end;} 

    public String toString(){ return "("+start+","+end+")"; } 
} 

などList<Interval>

[(0,4),(1,7),(6,10),(13,17),(20,100),(22,31),(60,65)] 

私はList<List<Interval>>の出力をしたい:

[[(0,4),(1,7),(6,10)],[(13,17)],[(20,100),(22,31),(60,65)]] 

私はこれをコーディングすることができますが、I Java 8のより機能的なアプローチを本当に楽しんでいて、Java 8ストリームを使ってこれを行う慣習的な方法があるかどうかを知りたい。

私は「」の「グループ化」スタイルを見てきましたが、実際には分類器でグループ化していないので適用されないようです。個々の要素のプロパティでは、これまでに計算されたグループに関連して各要素のプロパティを考慮する必要があります。

確かに機能的な言語でこれを行うための非クレイジーな方法があります(私は本当に機能的プログラマーではない人として話しますが、 - ))。 Java 8のストリームでどうすればいいですか?

+2

「this.start = end;」はあなたが望むものではないと思います。しかし、 'final'変数を使用してエラーがコンパイラによって直ちに検出されるようにするのは良いことです。 – Holger

+1

ところで、入力が次のようなときの出力はどうでしょうか?[((60,65)、(22,31)、(20,100)] '? 3つの間隔はすべて一緒にマージする必要がありますか?言い換えれば、入力間隔の順番が結果を変えるかもしれませんか? –

+1

@ Tagir Valeev:質問の前提条件(最初の文で)は、要素が開始点によってソートされるということです。任意のソリューションにソートステップを追加することで、その要件を緩和するのは簡単です。 – Holger

答えて

4

できません。ストリームはこの種の問題には適していません。ストリームは「以前の要素」の概念を持たず、任意の順序で要素を操作することが許されています。あなたはJavaでそれを行うことができます。確かに、関数型言語で行うことはできますが、それはあなたが使用していた関数型言語のデータ構造のようなストリームの動作を意味するものではありません。

+0

** upvoteだが代理人がいない – codeCogs

+2

すべての操作で要素を任意の順序で処理できるわけではありません。任意の順序を許可すると、たとえば'Collectors.toList()'、非常に難しいです... – Holger

+0

しかし、toListは任意の順序で要素を許可しますが、順序付けられた方法でアキュムレータを一緒にマージします。 –

4

あなたはgroupingByコレクターを勉強するときに正しい場所を見ていましたが、あなたは合併間隔に必要なロジックを提供しないことも正しいでしょう。しかし、彼らは概念的に以前の要素によって作られた状態に要素をマージしています。同様のコレクターを自分で実装する必要があります。要素はすでに開始インデックスによって事前にソートされている、あなたの仕様に依存

は、あなたが好きなことを行うことができます。

Comparator<Interval> byStart = Comparator.comparingInt(Interval::getStart); 
Comparator<Interval> byEnd = Comparator.comparingInt(Interval::getEnd); 
Collection<List<Interval>> merged = intervalList.stream().collect(
     () -> new TreeMap<Interval,List<Interval>>(byStart), 
     (map,i) -> { 
      Map.Entry<Interval,List<Interval>> e=map.floorEntry(i); 
      if(e!=null && Collections.max(e.getValue(), byEnd).getEnd()>=i.getStart()) 
       e.getValue().add(i); 
      else map.computeIfAbsent(i, x->new ArrayList<>()).add(i); 
     }, 
     (m1,m2) -> m2.forEach((i,list) -> { 
      Map.Entry<Interval,List<Interval>> e=m1.floorEntry(i); 
      if(e!=null && Collections.max(e.getValue(), byEnd).getEnd()>=i.getStart()) 
       e.getValue().addAll(list); 
      else m1.put(i, list); 
     }) 
    ).values(); 

これはCollectionではなくListを作成しますが、あなたは、単にのうちListを作成することができますそれ:

List<List<Interval>> list = new ArrayList<>(merged); 
Collectionが返されますが、長い時間のために結果を維持するのではなく、すぐにそれを処理しようとする場合は、その間違いなく行う必要があります

コレクタによって、TreeMapに必要以上のリソースを保持するビューがあります。

ほとんどの場合、ループベースのソリューションを使用する方がよいでしょう。

+0

ニースのソリューション! (1)私は、アキュムレータがすべてのストリーム要素を連続して実行するならば、コンバイナは役に立たないと思う(この解決法が動作する場合にはそうでなければならない)。 (2)合意:この場合、ループはより透明かつ効率的になりそうです。 – codeCogs

+1

@codeCogs:コンバイナはシーケンシャルストリームには使用されませんが、未来の驚きを避けるために、常に動作するコンバイナ機能を提供するための優れたコーディングスタイルです。正式にはそうしないと、API契約に違反することさえあります。このソリューションのコンバイナは正しく動作しますが、これを達成するためには非常に時間がかかります。これは、並列処理の利点を食い止めることができます。 – Holger

関連する問題