简体   繁体   English

使用流交替混合两个列表

[英]Mixing two lists alternately using stream

I implemented this using a loop, but I don't know how do that using a stream. 我使用循环实现了这个,但我不知道如何使用流。 We get two lists for example 1 2 3 and 4 5 6, after mixing the result will be 1 4 2 5 3 6. 我们得到两个列表,例如1 2 3和4 5 6,混合后结果将是1 4 2 5 3 6。

public <T> List<T> mixingList(List<T> list1, List<T> list2) {
    List<T> result = new ArrayList<>();

    int maxSize = list1.size() > list2.size() ? list1.size() : list2.size();

    for (int i = 0; i < maxSize; i++) {
        if (i < list1.size()) {
            result.add(list1.get(i));
        }

        if (i < list2.size()) {
            result.add(list2.get(i));
        }
    }

    return result;
}

I prepare testing for that. 我准备测试了。 There is 3 tests first - the same size the list second - with one empty list third - with the different size 首先是3个测试 - 第二个列表的大小相同 - 第三个是空列表 - 具有不同的大小

@Test
public void shouldReturnShakedList() {
    //given
    List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 3, 5));

    List<Integer> list2 = new ArrayList<>(Arrays.asList(2, 4, 6));

    //when
    List<Integer> results = new ShakeListUtil().mixingList(list1, list2);

    //then
    Assertions.assertThat(results).containsExactly(1, 2, 3, 4, 5, 6);
    Assertions.assertThat(results).hasSize(6);
}

@Test
public void shouldReturnJustList2IfList1IsEmpty() {
    //given
    List<Integer> list1 = new ArrayList<>();

    List<Integer> list2 = new ArrayList<>(Arrays.asList(2, 4, 6));

    //when
    List<Integer> results = new ShakeListUtil().mixingList(list1, list2);

    //then
    Assertions.assertThat(results).containsExactly(2, 4, 6);
    Assertions.assertThat(results).hasSize(3);

}

@Test
public void shouldReturnShakedListIfTheSizeListIsDifferent() {
    //given
    List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 3));

    List<Integer> list2 = new ArrayList<>(Arrays.asList(2, 4, 6));

    //when
    List<Integer> results = new ShakeListUtil().mixingList(list1, list2);

    //then
    Assertions.assertThat(results).containsExactly(1, 2, 3, 4, 6);
    Assertions.assertThat(results).hasSize(5);
}

Any idea how do that on java streams? 知道如何在java流上做到这一点?

Here is a solution using streams: 以下是使用流的解决方案:

public static <T> List<T> mixingList(List<T> list1, List<T> list2) {
    int shorter = Math.min(list1.size(), list2.size());
    int longer = Math.max(list1.size(), list2.size());
    Stream<T> firstPart = IntStream.range(0, shorter).mapToObj(x -> Stream.of(list1.get(x), list2.get(x))).flatMap(x -> x);
    if (longer > shorter) {
        Stream<T> secondPart = (list1.size() > list2.size() ? list1 : list2).subList(shorter, longer).stream();
        return Stream.concat(firstPart, secondPart).collect(Collectors.toList());
    } else {
        return firstPart.collect(Collectors.toList());
    }
}

The magic happens in mapToObj and flatMap . 魔术发生在mapToObjflatMap It maps every index to a stream of two list elements, one from each list given. 它将每个索引映射到两个列表元素的流,每个列表元素中给出一个列表元素。 And then it flattens the stream of streams with flatMap . 然后用flatMap平坦化流的流。

After that, if the two lists have different sizes, it gets the rest of the longer list and concatenates it to the end. 之后,如果两个列表具有不同的大小,它将获取较长列表的其余部分并将其连接到末尾。

One approach could be to use an IntStream to get a stream of list indices, map them to Optional s based on whether the list contains this index or not, and then resolve them, although, to be honest, I'm not sure this is more elegant than your original approach: 一种方法可能是使用IntStream获取列表索引流,根据列表是否包含此索引将它们映射到Optional s,然后解决它们,但是,老实说,我不确定这是比原来的方法更优雅:

public <T> List<T> mixingList(List<T> list1, List<T> list2) {
    int maxSize = Math.max(list1.size(), list2.size());

    return IntStream.range(0, maxSize)
            .mapToObj(i -> Stream.of(listIndexToOptional(list1, i), 
                                     listIndexToOptional(list2, i)))
            .flatMap(Function.identity())
            .filter(Optional::isPresent)
            .map(Optional::get)
            .collect(Collectors.toList());
}

private static <T> Optional<T> listIndexToOptional(List<T> list, int index) {
    return index < list.size() ? Optional.of(list.get(index)) : Optional.empty();
}

You can use split this into two parts. 您可以将其拆分为两部分。 First you get the min numbers of both lists and mix both lists until this index. 首先,您获得两个列表的最小数量,并将两个列表混合到此索引。 After that you append the remaining items in the larger list. 之后,您将其余项目附加到较大的列表中。 To combine both you can use Stream.concat() : 要将两者结合使用,您可以使用Stream.concat()

private static <T> List<T> mixingList(List<T> list1, List<T> list2) {
    int min = Math.min(list1.size(), list2.size());
    return Stream.concat(
            IntStream.range(0, min).boxed()
                    .flatMap(i -> Stream.of(list1.get(i), list2.get(i))),
            (list1.size() < list2.size() ? list2 : list1).stream().skip(min)
    ).collect(Collectors.toList());
}

Alternatively you can just use Stream.concat() while using Stream.flatMap() : 或者,您可以在使用Stream.concat()时使用Stream.flatMap()

private static <T> List<T> mixingList(List<T> list1, List<T> list2) {
    return IntStream.range(0, Math.max(list1.size(), list2.size())).boxed()
            .flatMap(i -> Stream.concat(
                    i < list1.size() ? Stream.of(list1.get(i)) : Stream.empty(),
                    i < list2.size() ? Stream.of(list2.get(i)) : Stream.empty()))
            .collect(Collectors.toList());
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM