如何在Java 8中使用concat获取不同的对象列表

时间:2016-03-23 00:55:33

标签: java java-8 java-stream concat distinct-values

我有2个Java类。

class A {
 String name;
 List<B> numbers;
}

class B {
 Integer number;
}

我希望获得A类的独特性并将其中的B列表连接起来。

e.g。假设我有一个包含以下对象的List。

List<A>{
 name = "abc"
 List<B>{1,2}

 name= "xyz"
 List<B>{3,4}

 name = "abc"
 List<B>{3,5}
}

结果应为:

List<A>{
 name = "abc"
 List<B>{1,2,3,5}

 name="xyz"
 List<B>{3,4}
}

任何帮助都将不胜感激。

注意:我想使用Java 8流来实现此功能。

由于

3 个答案:

答案 0 :(得分:3)

您可以使用toMap收藏家:

Collection<A> result = list.stream()
         .collect(Collectors.toMap(a -> a.name, a -> a, 
                      (a, b) -> {a.numbers.addAll(b.numbers); return a;}))
         .values();

您可以在此之后将结果复制到List(例如new ArrayList<>(result)),但由于我们不保留任何特定顺序,因此List不是很有用。在大多数情况下,Collection结果很好。

答案 1 :(得分:2)

我认为没有办法绕过地图。但是,您可以使用<%= yield(:twitter) %> 来隐藏它,但它实际上与Paul Boddington建议的代码和性能相同。

groupingBy

原始列表没有变异,您可以轻松更改合并列表的行为 - 例如,如果您想要删除重复项,只需在List<A> merge(List<A> input) { return input.stream() .collect(groupingBy(a -> a.name)) // map created here .entrySet() .stream() .map(entry -> new A( entry.getKey(), entry.getValue().stream() .flatMap(list -> list.numbers.stream()) .collect(toList()) // merging behaviour )).collect(toList()); } 之后添加.distinct()(请记住添加{ {1}} flatMap(list -> list.numbers.stream())}或以类似​​的方式,您只需添加equals即可对其进行排序(您必须使B实现B接口或仅使用.sorted()

以下是测试和导入的完整代码:

Comparable

修改

在评论中提出问题后,如果要在.sorted(Comparator<B>)子句中添加多个字段,则需要创建一个表示地图中键的类。如果您有不希望包含在键中的字段,则必须定义如何合并两个不同的值 - 类似于您对数字执行的操作。根据字段的不同,合并行为可以只选择第一个值并丢弃另一个(我在下面的代码中使用import org.junit.Test; import java.util.List; import static com.shazam.shazamcrest.MatcherAssert.assertThat; import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs; import static java.util.Arrays.asList; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toList; public class Main { class A { final String name; final List<B> numbers; A(String name, List<B> numbers) { this.name = name; this.numbers = numbers; } } class B { final Integer number; B(Integer number) { this.number = number; } } List<A> merge(List<A> input) { return input.stream() .collect(groupingBy(a -> a.name)) .entrySet() .stream() .map(entry -> new A( entry.getKey(), entry.getValue().stream() .flatMap(list -> list.numbers.stream()) .collect(toList()) )).collect(toList()); } @Test public void test() { List<A> input = asList( new A("abc", asList(new B(1), new B(2))), new A("xyz", asList(new B(3), new B(4))), new A("abc", asList(new B(3), new B(5))) ); List<A> list = merge(input); assertThat(list, sameBeanAs(asList( new A("abc", asList(new B(1), new B(2), new B(3), new B(5))), new A("xyz", asList(new B(3), new B(4))) ))); } } 完成的操作)。

groupingBy

答案 2 :(得分:1)

这是我的答案。我已为A添加了一个构造函数,使其更容易一些。

public class Main {

    static class A {
        String name;
        List<B> numbers;

        A(String name, List<B> numbers) {
            this.name = name;
            this.numbers = new ArrayList<>(numbers);
        }
    }

    static class B {
        Integer number;
    }

    static List<A> merge(List<A> list) {
        Map<String, List<B>> map = new LinkedHashMap<>();
        for (A a : list)
            map.computeIfAbsent(a.name, k -> new ArrayList<>()).addAll(a.numbers);
        return map.entrySet()
                  .stream()
                  .map(e -> new A(e.getKey(), e.getValue()))
                  .collect(Collectors.toList());
    }
}

几乎可以肯定有一种简单的方法可以将merge方法的前3行替换为以list.stream()...开头的内容,使整个方法成为单行。虽然我无法解决这个问题。也许其他人可以编辑这个答案显示如何?