2016-03-22 15 views
7

.NET'den Java'ya bir kod parçasını taşıyorum ve & eşlemesini azaltmak için kullanmak istediğim bir senaryoyu tökezledim.Java 8 akışı birleştirme ve birden çok değer döndürme

class Content 
{ 
    private String propA, propB, propC; 
    Content(String a, String b, String c) 
    { 
    propA = a; propB = b; propC = c; 
    } 
    public String getA() { return propA; } 
    public String getB() { return propB; } 
    public String getC() { return propC; } 
} 

List<Content> contentList = new ArrayList(); 
contentList.add(new Content("A1", "B1", "C1")); 
contentList.add(new Content("A2", "B2", "C2")); 
contentList.add(new Content("A3", "B3", "C3")); 

Ben daha çok benzeyen bazı kodlar bulabilirsiniz böylece contentlist içeriği ile akarsu ve Java Oldukça yeniyim sonucu

content { propA = "A1, A2, A3", propB = "B1, B2, B3", propC = "C1, C2, C3" } 

sahip bir sınıf dönebilirsiniz bir işlev yazmak istiyorum java

+1

İyi sorun bilgi ile kullanabilirsiniz. Ama şimdi, ne denedin? StackOverflow, başkalarının sizin için kod yazmasını sağlayan bir topluluk değildir. Bunun yerine, ne denediğinizi, neye sahip olduğunuzu ve neyi beklediğinizi gösteren bir [mcve] göndermeniz gerekir ve StackOverflow topluluğu, yanlış gittiğiniz yerleri belirlemeye ve çözümleri belirlemeye yardımcı olabilir. – AJNeufeld

+0

@AJNeufeld öneri için teşekkürler. Bu aynı zamanda, her gün kullanıyorum ama SO üzerinde bir soru göndermeye çalışıyorum. İçeriği eklemek için bir değişken ile temel bir döngü ile çalışıyorum. Ve stream üzerinde forEach kullanmayı denedim sonra Java anonim fonksiyonların foreach bloğu içinde değişken değiştirmeye izin vermediğini fark etti. Ayrıca, bir ulaşmak için bir koleksiyon yazmayı düşündüm, ama bunun da verimli olmayacağını fark ettim. – Vedanth

cevap

3

C# gibi daha düşük işlevler için BinaryOperator için uygun lambda kullanabilirsiniz.

Content c = contentList 
      .stream() 
      .reduce((t, u) -> new Content(
            t.getA() + ',' + u.getA(), 
            t.getB() + ',' + u.getB(), 
            t.getC() + ',' + u.getC()) 
        ).get(); 
+1

Bu çalışır, ancak birçok geçici 'İçerik 'nesnesi oluşturur. – AJNeufeld

+1

@AJNeufeld kabul etti, ancak uzay ve zaman arasındaki seçim. – mks

+1

Sadece 3 özellik ile, uygulamamın bir parçasıyım. Ancak daha genel bir durumda - daha fazla mülk, daha karmaşık özellikler, özel mülkler, vb. - en iyisi olabilir - ((t, u) -> yeni İçerik (t, u); "Content" nesnesinin kendisi, özel bir kurucu ile veya bilgiyi verimli bir şekilde bir araya getiren bir 'ContentCollector 'yazar. – AJNeufeld

4
static Content merge(List<Content> list) { 
    return new Content(
      list.stream().map(Content::getA).collect(Collectors.joining(", ")), 
      list.stream().map(Content::getB).collect(Collectors.joining(", ")), 
      list.stream().map(Content::getC).collect(Collectors.joining(", "))); 
} 

DÜZENLEME:

class Merge { 

    public static Collector<Content, ?, Content> collector() { 
     return Collector.of(Merge::new, Merge::accept, Merge::combiner, Merge::finisher); 
    } 

    private StringJoiner a = new StringJoiner(", "); 
    private StringJoiner b = new StringJoiner(", "); 
    private StringJoiner c = new StringJoiner(", "); 

    private void accept(Content content) { 
     a.add(content.getA()); 
     b.add(content.getB()); 
     c.add(content.getC()); 
    } 

    private Merge combiner(Merge second) { 
     a.merge(second.a); 
     b.merge(second.b); 
     c.merge(second.c); 
     return this; 
    } 

    private Content finisher() { 
     return new Content(a.toString(), b.toString(), c.toString()); 
    } 
} 

olarak kullanılır:

Content merged = contentList.stream().collect(Merge.collector()); 
3

ise Federico satır içi toplayıcı üzerine genişletilmesi, burada birleştirme İçerik nesnelere adanmış bir beton sınıfıdır lis üzerinde 3 kez tekrarlamak istemiyorsun t veya çok fazla Content orta nesneleri oluşturmak istemiyorum, sonra kendi uygulamasıyla akışı toplamak gerekir:

public static Content collectToContent(Stream<Content> stream) { 
    return stream.collect(
     Collector.of(
      () -> new StringBuilder[] { 
        new StringBuilder(), 
        new StringBuilder(), 
        new StringBuilder() }, 
      (StringBuilder[] arr, Content elem) -> { 
       arr[0].append(arr[0].length() == 0 ? 
         elem.getA() : 
         ", " + elem.getA()); 
       arr[1].append(arr[1].length() == 0 ? 
         elem.getB() : 
         ", " + elem.getB()); 
       arr[2].append(arr[2].length() == 0 ? 
         elem.getC() : 
         ", " + elem.getC()); 
      }, 
      (arr1, arr2) -> { 
       arr1[0].append(arr1[0].length() == 0 ? 
         arr2[0].toString() : 
         arr2[0].length() == 0 ? 
           "" : 
           ", " + arr2[0].toString()); 
       arr1[1].append(arr1[1].length() == 0 ? 
         arr2[1].toString() : 
         arr2[1].length() == 0 ? 
           "" : 
           ", " + arr2[1].toString()); 
       arr1[2].append(arr1[2].length() == 0 ? 
         arr2[2].toString() : 
         arr2[2].length() == 0 ? 
           "" : 
           ", " + arr2[2].toString()); 
       return arr1; 
      }, 
      arr -> new Content(
        arr[0].toString(), 
        arr[1].toString(), 
        arr[2].toString()))); 
} 

Bu toplayıcı ilk 3 boş StringBuilder nesneleri dizisi oluşturur. Sonra her Content öğesinin özelliğini karşılık gelen StringBuilder'a ekleyen bir biriktiriciyi tanımlar. Daha sonra, akım sadece paralel olarak işlendiğinde kullanılan ve daha önce birikmiş iki kısmi sonucu birleştiren bir birleştirme işlevini tanımlar. Son olarak, 3 StringBuilder nesnesini yeni bir Content örneğine dönüştüren bir sonlandırıcı işlevini de tanımlar; her özellik önceki adımların birikmiş dizelerine karşılık gelir.

Daha fazla bilgi için lütfen Stream.collect() ve Collector.of() javadocs'a bakın.

4

Bu tür görevlerle uğraşmanın en genel yolu, çoklu toplayıcıların sonuçlarını tek bir şekilde birleştirmektir. jOOL kütüphaneyi kullanma

, aşağıdakilere sahip olabilir:

Content content = 
    Seq.seq(contentList) 
     .collect(
     Collectors.mapping(Content::getA, Collectors.joining(", ")), 
     Collectors.mapping(Content::getB, Collectors.joining(", ")), 
     Collectors.mapping(Content::getC, Collectors.joining(", ")) 
     ).map(Content::new); 

Bu giriş listesinden bir Seq oluşturur ve 3 değerleri için bir tutucu basitçe olan bir Tuple3 oluşturmak için 3 verilen koleksiyoncuları bir araya getirir. Bu 3 değer daha sonra new Content(a, b, c) yapıcı kullanılarak Content içine eşlenir. Koleksiyoner, her Content'u a, b veya c değerine eşleştiriyor ve sonuçları bir ", " ile birbiriyle birleştiriyor.


üçüncü taraf yardımı olmadan, biz (bu 2 koleksiyoncular için aynı şeyi yapar StreamExpairing kollektör, temel alır) böyle kendi birleştirici kollektörü oluşturabilir. 3 toplayıcıyı argüman olarak alır ve toplanan 3 değerin sonunda bir son işlemci işlemi gerçekleştirir.

public interface TriFunction<T, U, V, R> { 
    R apply(T t, U u, V v); 
} 

public static <T, A1, A2, A3, R1, R2, R3, R> Collector<T, ?, R> combining(Collector<? super T, A1, R1> c1, Collector<? super T, A2, R2> c2, Collector<? super T, A3, R3> c3, TriFunction<? super R1, ? super R2, ? super R3, ? extends R> finisher) { 

    final class Box<A, B, C> { 
     A a; B b; C c; 
     Box(A a, B b, C c) { 
      this.a = a; 
      this.b = b; 
      this.c = c; 
     } 
    } 

    EnumSet<Characteristics> c = EnumSet.noneOf(Characteristics.class); 
    c.addAll(c1.characteristics()); 
    c.retainAll(c2.characteristics()); 
    c.retainAll(c3.characteristics()); 
    c.remove(Characteristics.IDENTITY_FINISH); 

    return Collector.of(
      () -> new Box<>(c1.supplier().get(), c2.supplier().get(), c3.supplier().get()), 
      (acc, v) -> { 
       c1.accumulator().accept(acc.a, v); 
       c2.accumulator().accept(acc.b, v); 
       c3.accumulator().accept(acc.c, v); 
      }, 
      (acc1, acc2) -> { 
       acc1.a = c1.combiner().apply(acc1.a, acc2.a); 
       acc1.b = c2.combiner().apply(acc1.b, acc2.b); 
       acc1.c = c3.combiner().apply(acc1.c, acc2.c); 
       return acc1; 
      }, 
      acc -> finisher.apply(c1.finisher().apply(acc.a), c2.finisher().apply(acc.b), c3.finisher().apply(acc.c)), 
      c.toArray(new Characteristics[c.size()]) 
      ); 
} 

ve son olarak

Content content = contentList.stream().collect(combining(
    Collectors.mapping(Content::getA, Collectors.joining(", ")), 
    Collectors.mapping(Content::getB, Collectors.joining(", ")), 
    Collectors.mapping(Content::getC, Collectors.joining(", ")), 
    Content::new 
));