2016-10-27 6 views
2

Java 8ストリームを調べています。Java 8ストリームを使用してTreeデータモデルを構築することは可能ですか

私は現在、各レコードが親子関係を記述するファイルをストリーミングすることによって古典的なツリー構造を構築しようとしています。

私は私のソリューションは、以下の例に似ている入力データレコードからツリーを構築できるようにしたいと思います: -

Parent A - Child B 
Parent A - Child C 
Parent A - Child D 
Parent A - Child E 
Parent B - Child F 
Parent B - Child G 
Parent C - Child H 
Parent D - Child I 
Parent G - Child J 

私は例えばLinkedHashMap<String, List<String>>

を構築するためにどちらかの希望

A - B, C, D, E 
B - F, G 
C - H 
D - I 
G - J 

で終わる私が手に最も近い

Map<String, List<String>> map = stream.sorted().map(line -> line.split("-")).flatMap(line -> Arrays.stream(line)).collect(Collectors.toMap(Function.identity(), Arrays::asList)); 

それとも

public class Node { 

    private final String name; 
    private Node parent; 
    private List<Node> children = new LinkedList<>(); 

} 

Node値オブジェクトは、すべてのツリーノードを作成し使用して重複キーとし、フル装備子供ノードリストで失敗します私の入力ファイルのストリームから直接。

+1

あなたの最初の例では、より多くの木よりも複数の値を持つマップのようなものです。あなたは何を目指していますか?あなたの2番目の例に似たマルチマップまたはツリー構造ですか?ツリー構造を目指す場合、入力データは実際に木のような構造(例えば単一のルートノード、接続された非循環型)を保証しますか? –

+0

@NándorElődFekete複数値のマップまたはツリーのいずれかが私の要件を満たします。私のファイル入力データは、単一のルートノードと動的な子ノードの数を持つ古典的なマルチノードマップを保証します。 – Hector

答えて

3

これはgroupingByコレクターのための仕事です:

import static java.util.stream.Collectors.*; 

Pattern ptrn = Pattern.compile("Parent (.*) - Child (.*)"); 

Map<String, List<String>> map = data.stream() 
     .sorted() 
     .map(ptrn::matcher) 
     .filter(Matcher::find) 
     .collect(groupingBy(
       m -> m.group(1), 
       LinkedHashMap::new , 
       mapping(m -> m.group(2), toList()) 
     )); 
3

子供を集約するマージラムダを追加します。


あなたのノードはparentフィールドを持っていなかった場合、あなたはより直接的にノードを得ることができます:

List<Node> nodes = ist.stream().sorted() 
    .map(line -> line.split("\\s*-\\s*")) 
    .collect(groupingBy(a -> a[0])) 
    .entrySet() 
    .stream() 
    .map(e -> new Node(e.getKey()[0], e.getValue().stream() 
     .map(a -> new Node(a[1], null)) 
     .collect(toList()))) 
    .collect(toList()); 

初期のグループ化をListへの会話がないので、はるかに簡単です - 分割された元の配列はそのまま残されます。

免責事項:コードがコンパイルや仕事、それが自分の携帯電話上で使い込まれたように(それが動作する合理的な可能性があります)しないことがあり

+1

Nodeクラスに 'parent'フィールドがあると、マップからノードを作成するのが難しくて面倒です。それがなければ、マップを簡単にストリームしてノードのリストを作成することができます。 – Bohemian

+0

どのようにノードをリンクして、親フィールドなしでツリーを構築できますか? – Hector

+0

@Hector子供は既に子供リストに載っていることで "リンク"しています。任意の子から親に移動する必要がある場合を除いて(珍しいことですが、使用されることはありません)、親フィールドは必要ありません。おそらく、状況をデータベースタプルと外部キーで比較していますが、それはアプリケーションの世界には当てはまりません。 – Bohemian

2

あなたはマルチマップを目指している場合は、使用することができます次:ここ

Map<String, Collection<String>> result = stream //stream of lines 
    .sorted() 
    .map(line -> line.split("\\s*-\\s*")) 
    .collect(
     Collectors.toMap(
      (String[] arr) -> arr[0], 
      (String[] arr) -> Collections.singleton(arr[1]), 
      (u, v) -> { 
       Collection<String> merged = new LinkedHashSet<>(u); 
       merged.addAll(v); 
       return merged; 
      }, 
      LinkedHashMap::new 
     ) 
    ); 

キーは、重複キーのケースを処理するために複数の値を持つ、すなわち、キーを値としてコレクションやマージ機能を使用するカスタムマップコレクターです。単純なHashMapHashSetLinkedHashMapLinkedHashSetに配置することができます。要素の順序が気にしない場合は、sorted()操作を削除することもできます。

関連する問題