2016-11-15 4 views
4

すべてのオブジェクトに名前フィールドがあるようなカタログのようなオブジェクト階層があります。lambda Java 8、フィルター操作の結果のフィールドであるリストをマップする方法

class A { 
    List<A> list; 
    String name; 
} 

A{A{A{A...}AA},A{AAA},A{AAA}} // the depth is finite (~4) 

私は与えられた名前のための任意の親要素の子の名前(a.getName())のリストを返すメソッドのセットを提供したいと思います。 だから、レベル1のために、私は

a.getAs().stream().map(a1 -> a1.getName()).collect(Collectors.toList()); 

レベル2を持っている私はすでにとトラブルています

a1.getAs().stream().filter(a2 -> a2.getName() == name) 

は今、私は同様にアクセスし、自分の名前にマッピングしたいが、私はどのように

知りません

EDIT: 私は、3つめのレベルから、単一の名前を提供するだけではリストを見つけることができないことに気付きました。子リストを収集できるノードにナビゲートできるようにするには、各レベルの名前が必要です。 一方、私はすべてのオブジェクトを1つのセットに保ち、IDでそれらにアクセスできます。彼らは依然としてお互いを参照しています。一方、根本的な要素を知らないことによって、私は構造を正しく得ることができませんでした。

私はこの問題を再考する必要があると思います。

+0

最後の明細書でフィルタを使用して達成しようとしていることを説明できますか? –

+0

@Andrew Tobilko:はい、そうです。 Stream APIはより効率的で、読み易さが向上し、並列で実行できます。 – mooor

答えて

2

あなたはこのようにそれを行うことができます。

public static List<String> getChildNames(A node, String... path) { 
    Stream<A> s = node.getAs().stream(); 
    for(String name: path) 
     s = s.filter(a -> a.getName().equals(name)).flatMap(a -> a.getAs().stream()); 
    return s.map(A::getName).collect(Collectors.toList()); 
} 

しかしAノードの下の名前が一意である場合は、あなたの代わりにList<A>の、実際の子に子の名前からMap<String,A>、マッピングを維持することを検討すべきです。これにより、ユニークな名前/ IDを使用して簡単にnode.get(name1).get(name2)というパスをたどることができます。上記の方法のロジックは、ユニークな結果を持つ必要のないパターンマッチングを組み込む場合には、依然として有効です。

public static List<String> getChildNames(A node, String... pathPatterns) { 
    Stream<A> s = node.getAs().stream(); 
    for(String namePattern: pathPatterns) { 
     Pattern compiledPattern = Pattern.compile(namePattern); 
     s = s.filter(a -> compiledPattern.matcher(a.getName()).find()) 
      .flatMap(a -> a.getAs().stream()); 
    } 
    return s.map(A::getName).collect(Collectors.toList()); 
} 
+0

['Pattern.asPredicate()'](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#asPredicate--):-) –

+1

@Didier L :残念ながら、ここではあまり役に立ちませんが、まだ 'getName()'と組み合わせる必要があります。私は本当に「述語」に「関数」を使ってそれを構成する方法を持っていたと思っています...コードベースでは、このような実用的な方法がありますが、SOの例は自己完結型でなければなりません。 – Holger

+0

名前のユニークは同じリスト内でのみ保証されます。上記のコードはテストされており、最初の要件に対する有効な解決策です。 flatMapの連鎖アプローチは部分的に@Andrew Tobilkoによって言及されましたが。私は誰が答えを受け入れるのか分からないのですか? – mooor

2

それだけで、階層の1つのレベルのために働く:

public List<A> getSubcategoriesByParentName(A category, String name) { 
    return category.getSubcategories() 
        .stream() 
        .filter(subcategory -> subcategory.getName().equals(name)) 
        .collect(Collectors.toList()); 
} 

次のレベルを達成するために、あなたはflatMapを使用することができます。

category.getSubcategories().stream() 
     .flatMap(s -> s.getSubcategories().stream()) 
     .filter(s -> s.getName().equals(name)) 
     .collect(Collectors.toList()); 

あなたが見ることができるように、する必要があります再帰、それはストリームAPIの仕事ではありません。

もちろん、深さを知っているので、すべてのレベルにアクセスすることができます(flatMap(s -> s.getSubcategories().stream())を数回使用しています)が、見苦しくなります。

+1

'flatmap'と' filter'部分をループに入れて、最後の部分だけを集めることで、より多くの階層構造を追加できます。 –

+1

深度は有限(最大で4)であると言われているので、再帰なしで最大4つの 'flatMap'ステップまで連鎖することに問題はありません。 – Holger