使用Stream API合并两个由N个元素交替的列表

时间:2017-08-03 20:21:38

标签: java list java-stream

我需要合并两个列表,每个列表中的N个元素交替:从第一个列表中取出前N个元素,从第二个列表中取出前N个元素,在第一个列表中的N个元素的第二部分之后,从N个元素中取出第二部分从第二个清单等。如果一个列表比另一个列表长,则将较长列表中的其余元素附加到结果列表中。

例如,第一个列表:1, 2, 3, 4, 第二个清单:10, 11, 12, 13, 14, 15, 16, 17

合并N = 2的交替结果为1, 2, 10, 11, 3, 4, 12, 13, 14, 15, 16, 17

这种合并可以通过以下方式实施:

List<Integer> list1 = Arrays.asList(1, 2, 3, 4);
List<Integer> list2 = Arrays.asList(10, 11, 12, 13, 14, 15, 16, 17);
int alternatingNumber = 2;
List<Integer> mergedList = alternatingMerge(list1, list2, alternatingNumber);


public static <T> List<T> alternatingMerge(List<T> list1, List<T> list2, int alternatingNumber) {
    List<T> result = new ArrayList<T>();
    int size = Math.max(list1.size(), list2.size());

    for (int outerIndex = 0; outerIndex < size; outerIndex += alternatingNumber) {
        for (int innerIndex = 0; innerIndex < alternatingNumber; innerIndex++) {
            if (outerIndex + innerIndex < list1.size()) result.add(list1.get(outerIndex + innerIndex));
        }

        for (int innerIndex = 0; innerIndex < alternatingNumber; innerIndex++) {
            if (outerIndex + innerIndex < list2.size()) result.add(list2.get(outerIndex + innerIndex));
        }
    }

    return result;
}

用于交替1个元素(alternatingNumber = 1)的类似合并算法描述here,但我需要为任何交替数量实现通用逻辑。

是否有可能以某种方式使用Stream API?

2 个答案:

答案 0 :(得分:0)

我使用wrap / process / unwrap模式用((entryIndex / alternateStep),entry)封装每个条目。

包装的元素具有可比性,因此当按照自然顺序对流进行排序时,它们将按其位置(entryIndex / alternateStep)进行排序。

在最后一步中,我已将entires流收集到要返回的List中。

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

class Program
{
    static <T> List<T> alternateElementsBynStep (List<? extends T> flist, List<? extends T> slist, int n)
    {
        class Wrapper implements Comparable<Wrapper>
        {
            int position;
            T value;

            Wrapper (int position, T value)
            {
                this.position = position;
                this.value = value;
            }

            T getValue ()
            {
                return value;
            }

            @Override
            public int compareTo(Wrapper o) {
                return Integer.compare(position, o.position);
            }
        }

        Function<List<? extends T>, Stream<? extends Wrapper>> wrap =
            seq ->  IntStream.range(0, seq.size())
                        .mapToObj(i -> new Wrapper(i / n, seq.get(i)))
        ;

        return Stream.concat(wrap.apply(flist), wrap.apply(slist))
            .sorted()
            .map(Wrapper::getValue)
            .collect(Collectors.toList())
        ;
    }

    public static void main(String[] args)
    {
        System.out.println(Arrays.toString(alternateElementsBynStep(Arrays.asList(1, 2, 3, 4), Arrays.asList(10, 11, 12, 13, 14, 15, 16, 17), 2).toArray()));

        // output: [1, 2, 10, 11, 3, 4, 12, 13, 14, 15, 16, 17]
    }
}

答案 1 :(得分:0)

Guava提供了一种方法Streams.zip,该方法允许一次一个元素地处理两个流。以下代码将此方法应用于交替合并两个列表的情况:

public static <T> Stream<T> streamAlternatingInParts(List<T> first, List<T> second, int partSize) {
    Stream<Stream<T>> zipped = zip(partitioning(first, partSize), partitioning(second, partSize));
    Stream<T> alternating = zipped.flatMap(Function.identity());

    int lengthFirst = first.size();
    int lengthSecond = second.size();
    if (lengthFirst != lengthSecond) {
        if (lengthFirst > lengthSecond) {
            return Stream.concat(alternating, first.subList(lengthSecond, lengthFirst).stream());
        }
        return Stream.concat(alternating, second.subList(lengthFirst, lengthSecond).stream());
    }

    return alternating;
}

public static <T> Stream<Stream<T>> partitioning(List<T> list, int partSize) {
    int end = (list.size() + partSize - 1) / partSize;
    return IntStream.range(0, end)
            .mapToObj(i -> list.subList(partSize * i, Math.min(partSize * i + partSize, list.size())).stream());
}

public static <T> Stream<T> zip(Stream<T> first, Stream<T> second) {
    return zip(() -> StreamSupport.stream(first.spliterator(), false), () -> StreamSupport.stream(second.spliterator(), false));
}

public static <T> Stream<T> zip(Supplier<Stream<T>> first, Supplier<Stream<T>> second) {
    return Streams.zip(first.get(), second.get(), Stream::of).flatMap(Function.identity());
}

partioning根据此answer
zip根据answer

适用于问题中给出的示例:

public static void main(String[] args) {
    List<Integer> list1 = Arrays.asList(1, 2, 3, 4);
    List<Integer> list2 = Arrays.asList(10, 11, 12, 13, 14, 15, 16, 17);
    List<Integer> alternatingInParts = streamAlternatingInParts(list1, list2, 2).collect(Collectors.toList());
    System.out.println(Iterables.toString(alternatingInParts));
}

输出为:

[1, 2, 10, 11, 3, 4, 12, 13, 14, 15, 16, 17]

由于streamAlternatingInParts返回了Stream,因此可以继续进行流式传输。