2016-03-22 17 views
7

Tôi đang chuyển một đoạn mã từ .NET sang Java và tình cờ gặp một tình huống mà tôi muốn sử dụng luồng để ánh xạ & giảm.Dòng Java 8 tham gia và trả lại nhiều giá trị

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")); 

Tôi muốn viết một hàm mà có thể stream xuyên qua các nội dung của contentlist và trả về một lớp học với kết quả

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

Tôi khá mới để Java, do đó bạn có thể tìm thấy một số mã tương tự hơn như C# hơn java

+1

mô tả vấn đề tốt. Nhưng bây giờ, bạn đã thử cái gì? StackOverflow không phải là một cộng đồng để người khác viết mã cho bạn. Thay vào đó, bạn cần đăng một [mcve] hiển thị những gì bạn đã thử, những gì bạn có và những gì bạn mong đợi và cộng đồng StackOverflow có thể cố gắng giúp xác định nơi bạn đã đi sai và chỉ ra các giải pháp. – AJNeufeld

+0

@AJNeufeld cảm ơn đề xuất. Đây cũng là lần đầu tiên tôi cố gắng đăng câu hỏi trên SO mặc dù sử dụng nó mỗi ngày. Tôi có nó làm việc với một vòng lặp cơ bản với một biến để nối thêm nội dung. Và tôi đã thử sử dụng forEach trên luồng rồi nhận ra rằng các hàm ẩn danh Java không cho phép sửa đổi biến bên trong khối foreach. Ngoài ra tôi đã suy ngẫm về việc viết một thu thập để đạt được một nhưng nhận ra nó sẽ không hiệu quả hoặc. – Vedanth

Trả lời

3

Bạn có thể sử dụng lambda phù hợp cho BinaryOperator trong chức năng giảm.

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

Nó hoạt động, nhưng nó tạo ra nhiều đối tượng 'Nội dung' tạm thời. – AJNeufeld

+1

@AJNeufeld đã đồng ý, nhưng đó là sự lựa chọn giữa không gian và thời gian. – mks

+1

Chỉ với 3 thuộc tính, tôi là một phần trong quá trình triển khai. Nhưng trong một trường hợp tổng quát hơn - với nhiều thuộc tính hơn, thuộc tính phức tạp hơn, thuộc tính riêng, v.v. - tốt nhất có thể là 'reduce ((t, u) -> Nội dung mới (t, u);' ủy nhiệm việc hợp nhất với chính đối tượng 'Nội dung' với một hàm tạo chuyên biệt, hoặc thực sự viết một' ContentCollector' tích lũy thông tin một cách hiệu quả. – 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(", "))); 
} 

EDIT: Mở rộng trên thu inline Federico của, đây là một lớp bê tông dành riêng cho việc sáp nhập đối tượng Nội dung:

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()); 
    } 
} 

Được sử dụng như:

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

Nếu bạn không muốn lặp lại 3 lần so với lis t, hoặc không muốn tạo quá nhiều Content đối tượng trung gian, sau đó bạn sẽ cần phải thu thập các dòng với thực hiện của riêng bạn:

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()))); 
} 

nhà sưu tập này đầu tiên tạo ra một mảng của 3 trống StringBuilder đối tượng. Sau đó, xác định bộ tích lũy gắn thêm mỗi thuộc tính của phần tử Content vào StringBuilder tương ứng. Sau đó, nó định nghĩa một hàm kết hợp chỉ được sử dụng khi luồng được xử lý song song, kết hợp hai kết quả từng phần được tích luỹ trước đó. Cuối cùng, nó cũng định nghĩa một hàm kết thúc để biến đổi các đối tượng 3 StringBuilder thành một thể hiện mới của Content, với mỗi thuộc tính tương ứng với các chuỗi tích lũy của các bước trước đó.

Vui lòng kiểm tra Stream.collect()Collector.of() javadocs để tham khảo thêm.

4

Cách chung nhất để giải quyết các tác vụ như vậy là kết hợp kết quả của nhiều người sưu tập thành một bộ duy nhất.

Sử dụng thư viện jOOL, bạn có thể có những điều sau đây:

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); 

này tạo ra một Seq từ danh sách đầu vào và kết hợp 3 nhà sưu tập được để tạo ra một Tuple3, mà chỉ đơn giản là một người giữ cho 3 giá trị. Sau đó, 3 giá trị đó được ánh xạ thành một Content bằng cách sử dụng hàm tạo new Content(a, b, c). Bản thân người sưu tập chỉ đơn giản là ánh xạ mỗi Content vào giá trị a, b hoặc c và tham gia các kết quả cùng nhau được tách riêng với ", ".


Nếu không có bên thứ ba giúp đỡ, chúng ta có thể tạo ra thu kết hợp của chúng ta như thế này (điều này được dựa trên StreamExpairing nhà sưu tập, mà làm điều tương tự cho 2 nhà sưu tập). Phải mất 3 nhà sưu tập làm đối số và thực hiện một hoạt động kết thúc trên kết quả của 3 giá trị được thu thập.

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()]) 
      ); 
} 

và cuối cùng là sử dụng nó với

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 
)); 
Các vấn đề liên quan