简体   繁体   中英

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.

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

@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?

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 . 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 .

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:

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() :

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() :

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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