Streams - 合并2个列表

时间:2016-04-19 16:32:09

标签: java java-8 java-stream

使用Streams合并2个列表的方法面临的问题。目前,当我使用Stream.concat时,它会添加到列表的底部,这不是我想要的。

的List1:

  • [ABC,123,456]
  • [DEF,234,567]
  • [GHI,345,678]

列表2:

  • [ABC,789,012]
  • [DEF,890,123]
  • [GHI,901,234]

理想合并输出:

  • [ABC,123,456,789,012]
  • [DEF,234,567,890,123]
  • [GHI,345,678,901,234]

如果上述情况不可行,那么下面也可以接受

  • [ABC,123,456,ABC,789,012]
  • [DEF,234,567,DEF,890,123]
  • [GHI,345,678,GHI,901,234]

我目前得到的输出,这不是我想要的

  • [ABC,123,456]
  • [DEF,234,567]
  • [GHI,345,678]
  • [ABC,789,012]
  • [DEF,890,123]
  • [GHI,901,234]

代码:

List<List<XSSFCell>> listData1 = list1.stream().skip(1).collect(Collectors.toList());
    List<List<XSSFCell>> listData2 = list2.stream().skip(1).collect(Collectors.toList());
Stream stream = Stream.concat(listData1.stream(),listData2.stream());

希望我面临的问题很明确,等待指导。

此问题已标记为此question的副本但无法找到解决方案中提到的StreamUtils.zip方法。该问题的第一个答案是,各州自行承担风险。这可以使用标准的可用库来完成,还是我可以知道如何在StreamUtils中获得zip方法

1 个答案:

答案 0 :(得分:0)

这一切都取决于你想要获得多么复杂,但长话短说,你必须每次都为你的第二个列表创建一个新的流,所以你不能给两个流 - - 您可以获得一个流(您采取行动)和Collection / List,但不能获得两个流。这是因为如果没有抛出第二个流被关闭或运行的异常,你就不能stream1.forEach(stream2)

这是我的设置:

public static void main(String[] args) {
    Collection<List<String>> list1 = new ArrayList<>(5);
    Collection<List<String>> list2 = new ArrayList<>(5);

    list1.add(Arrays.stream(new String[] {"ABC", "123", "456"}).collect(Collectors.toList()));
    list1.add(Arrays.stream(new String[] {"DEF", "234", "567"}).collect(Collectors.toList()));
    list1.add(Arrays.stream(new String[] {"GHI", "345", "678"}).collect(Collectors.toList()));
    list2.add(Arrays.stream(new String[] {"ABC", "789", "012"}).collect(Collectors.toList()));
    list2.add(Arrays.stream(new String[] {"DEF", "890", "123"}).collect(Collectors.toList()));
    list2.add(Arrays.stream(new String[] {"GHI", "901", "234"}).collect(Collectors.toList()));
}

现在,根据您需要做多少检查以及知道两个流都有匹配键的条目(这里的地图确实比两个集合更好),以及你可以跳过很多错误检查,等等:

private static Stream<List<String>> combineTwoStreams(Collection<List<String>> list1,
        Collection<List<String>> list2) {
    Map<String, List<String>> newCollection = new LinkedHashMap<>();

    list1.stream()
    // Run through each entry in list1
            .forEach(row -> {
                // Find the entries in list2 that have the same 'key' and add them to newCollection
                    list2.stream().filter(row2 -> row.get(0).equals(row2.get(0))).forEach(row2 -> {
                        String key = row.get(0);

                        // Pull from newCollection
                            List<String> fromNewCollection = newCollection.get(key);
                            // If there wasn't an entry in newCollection for the key, add one now and pre-populate
                            // with list1's data
                            if (Objects.isNull(fromNewCollection)) {
                                fromNewCollection = new LinkedList<>(row);
                                newCollection.put(key, fromNewCollection);
                            }

                            // Add list2's data to the new collection (note that we can do an addAll instead
                            row2.subList(1, row2.size()).forEach(fromNewCollection::add);
                        });
                });

    // Return a Stream of our values
    return newCollection.values().stream();
}

现在,如果你还要检查list2的东西,你可能想要这个:

private static Stream<List<String>> combineTwoStreams(Collection<List<String>> list1, Collection<List<String>> list2) {
    Map<String, List<String>> newCollection = new LinkedHashMap<>();

    list1.forEach(row -> newCollection.put(row.get(0), row));
    list2.forEach(row -> {
        String key = row.get(0);

        // Pull from newCollection
        List<String> fromNewCollection = newCollection.get(key);
        // If there wasn't an entry in newCollection for the key, add one now and pre-populate
        // with list1's data
        if (Objects.isNull(fromNewCollection)) {
            fromNewCollection = new LinkedList<>(row);
            newCollection.put(key, fromNewCollection);
        } else
            // Add list2's data to the new collection (note that we can do an addAll instead
            row.subList(1, row.size()).forEach(fromNewCollection::add);
    });

    // Return a Stream of our values
    return newCollection.values().stream();
}

如果您想要防止重复,LinkedHashSet很棒,那么您可能newCollectionMap<String, Set<String>> newCollection = new LinkedHashMap<>();,然后您只需LinkedHashSet

最后,如果你想让自己睁大眼睛:

private static Stream<List<Object>> combineTwoStreams(Collection<List<String>> list1, Collection<List<String>> list2) {
    Map<String, List<String>> map1 = // Turn our first list into map map
    list1.stream().collect(Collectors.toMap(row1 -> row1.get(0), row1 -> row1));
    Map<String, List<String>> map2 = // Turn our second list into a map
    list2.stream().collect(Collectors.toMap(row2 -> row2.get(0), row2 -> row2.subList(1, row2.size())));

    return Stream
            .of(map1, map2)
            // Give me the EntrySets as a stream
            .flatMap(m -> m.entrySet().stream())
            // Collect it all down
            .collect(
            // Group together things by the "key" so that we have a Map<String,List<List<String>>>
                    Collectors.groupingBy(
                            Map.Entry::getKey,
                            Collectors.mapping(Map.Entry::getValue,
                            // Merge the lists within each list down so that we have
                            // Map<String,List<String>>
                                    Collectors.collectingAndThen(
                                            Collectors.toList(),
                                            row -> row.stream().flatMap(Collection::stream)
                                                    .collect(Collectors.toList())))))
            // Get the values from our Map
            .values()
            // Return the stream of List<String>
            .stream();
}