如何判断列表是否是java8流的另一个子序列?

时间:2017-02-17 08:23:42

标签: java functional-programming java-8 java-stream

例如,我有一个长列表[1, 2, 3, ..., 10],还有一个短列表[1, 3, 6],然后我可以说短的列表是另一个的子序列。另一方面,列表[1 6 3]不是因为它违反了订单约束。

以下是此问题的java7样式代码:

List<Integer> sequence = Arrays.asList(1, 3, 6);
List<Integer> global = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Iterator<Integer> iterGlobal = global.iterator();
boolean allMatch = true;
for(Integer itemSequence: sequence) {
    boolean match = false;
    while(iterGlobal.hasNext()) {
        if(itemSequence.equals(iterGlobal.next())) {
            match = true;
            break;
        }
    }
    if(!match) {
        allMatch = false;
        break;
    }
}
System.out.println(allMatch); //=> true

我希望找到一个java8流样式来实现相同的结果。

5 个答案:

答案 0 :(得分:6)

很难找到真正的功能性解决方案,即不包含可变状态。迄今为止所有答案都包含可变状态这一事实可以说明这一点。

此外,没有List.indexOf(T object, int startIndex)操作。为了说明它有多么有用,让我们通过辅助方法来定义它:

public static int indexOf(List<?> list, int startIndex, Object o) {
    if(startIndex!=0) list=list.subList(startIndex, list.size());
    int ix=list.indexOf(o);
    return ix<0? -1: ix+startIndex;
}

如果这是一个问题,很容易找到没有临时对象的替代实现

现在,使用可变状态的简单解决方案是:

boolean allMatch = sequence.stream().allMatch(new Predicate<Integer>() {
    int index = 0;
    public boolean test(Integer t) {
        return (index = indexOf(global, index, t)) >=0;
    }
});

没有可变状态的功能解决方案需要在两个列表中保存两个位置的值类型。当我们使用int[2]数组时,解决方案将是:

boolean allMatch = Stream.iterate(
        new int[]{ 0, global.indexOf(sequence.get(0)) },
        a -> new int[] { a[0]+1, indexOf(global, a[1], sequence.get(a[0]+1)) }
    )
    .limit(sequence.size())
    .allMatch(a -> a[1]>=0);

答案 1 :(得分:4)

我是提问者,我首先回答我的问题只是为了标记:

List<Integer> sequence = Arrays.asList(1, 3, 6);
List<Integer> global = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

Iterator<Integer> iter = global.iterator();
boolean subSequence = sequence.stream().allMatch(itemSequence -> {
    return Stream.generate(iter::next)
            .anyMatch(itemGlobal -> itemSequence.equals(itemGlobal));
});
System.out.println(subSequence);

它适用于序列列表[1,3,6],而序列[1,6,3]则抛出错误java.util.NoSuchElementException。这不是我最终想要达到的目标。

答案 2 :(得分:2)

我认为你非常接近解决方案(我甚至没有考虑像这样的Iterator,所以对你来说是一个加号)。问题是 Stream.generate 是一个无限的流。

我已经改变了你的代码。

    Iterator<Integer> iter = global.iterator();

    boolean subSequence = sequence.stream().allMatch(itemSequence -> {
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iter, Spliterator.ORDERED), false)
                .anyMatch(itemGlobal -> itemSequence.equals(itemGlobal));
    });
    System.out.println(subSequence);

答案 3 :(得分:2)

@Eugene的@ Run的答案的变体将涉及在全局List&lt;&gt;上调用Iterable :: spliterator。 value,然后将结果应用于StreamSupport :: stream:

ggplot()

答案 4 :(得分:0)

在看到霍尔格回答后,我会再添加一个选项;但这只适用于jdk-9 Stream.iterate

我已经以相同的方式定义了一个辅助方法,有点不同:

private static int fits(List<Integer> global, int elementIndex, int element) {
    return global.indexOf(element) >= elementIndex ? global.indexOf(element) : -1;
}

然后只使用int[2]

boolean allMatch = Stream.iterate(new int[] { 0, 0 },
            array -> array[0] < sequence.size() && array[1] >= 0,
            array -> new int[] { array[0] + 1, fits(global, array[1], sequence.get(a[0])) })
            .allMatch(array -> array[0] >= array[1]);

修改 Holger是对的,这只适用于非重复。

我也可以将它写成重复项,但是现在需要调用fits两次才会受到影响。

 boolean allMatch = Stream.iterate(new int[] { 0, 0 },
            a -> {
                return a[0] == sequence.size() ? false : fits(global, sequence.get(a[0])) >= a[1];
            },
            a -> {
                int nextFits = fits(global, sequence.get(a[0]));
                return new int[] { a[0] + 1, nextFits > a[1] ? nextFits + 1 : -1 };
            })
            .count() == sequence.size();