简体   繁体   English

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

[英]How to judge whether a list is a sub sequence of another with java8 stream?

For example, I have a long list [1, 2, 3, ..., 10] , and a short one [1, 3, 6] , then I can tell that the short one is the subsequence of another. 例如,我有一个很长的列表[1, 2, 3, ..., 10]和一个短的[1, 3, 6] ,然后我可以说短的是另一个的子序列。 On the other hand, the list [1 6 3] is not because it against the order constraint. 另一方面,列表[1 6 3]不是因为它违反了订单约束。

Below is my java7 style code for this question: 下面是我这个问题的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

And my wish is to find a java8 stream style to achieve the same result. 而我的愿望是找到一个java8流样式来实现相同的结果。

Real functional solutions, ie not incorporating mutable state, are hard to find. 真正的功能性解决方案,即不包含可变状态,很难找到。 This is best illustrated by the fact that all answer so far incorporate mutable state. 迄今为止所有答案都包含可变状态这一事实最能说明这一点。

Further, there is no List.indexOf(T object, int startIndex) operation. 此外,没有List.indexOf(T object, int startIndex)操作。 To illustrate, how useful it would be, let define it via helper method: 为了说明它是多么有用,让我们通过辅助方法来定义它:

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

It would be easy to find an alternative implementation without a temporary object, if that's a concern 如果这是一个问题,那么很容易找到没有临时对象的替代实现

Now, a simple solution using mutable state would be: 现在,使用可变状态的简单解决方案是:

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

A functional solution without mutable state requires a value type holding two positions within the two lists. 没有可变状态的功能解决方案需要在两个列表中保持两个位置的值类型。 When we use an int[2] array for that, the solution would be: 当我们使用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);

I am the questioner and I answer my question firstly just for mark: 我是提问者,我首先回答我的问题只是为了标记:

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

It work well for sequence list [1, 3, 6], while for sequence [1, 6, 3] throwing a error java.util.NoSuchElementException. 它适用于序列列表[1,3,6],而序列[1,6,3]则抛出错误java.util.NoSuchElementException。 This is not what I finally want to reach. 这不是我最终想要达到的目标。

I think that you got really close to the solution (I did not even think about a Iterator like this, so a plus one to you). 我认为你非常接近解决方案(我甚至没有考虑像这样的Iterator ,所以对你来说是一个加号)。 The problem is that Stream.generate is an infinite stream. 问题是Stream.generate是一个无限的流。

I've changed your code just a bit. 我已经改变了你的代码。

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

A variant of @Eugene's variant of @Run's answer would involve calling Iterable::spliterator on the global List<> value, then apply the result to StreamSupport::stream: @ Eugene的@ Run的答案的变体将涉及在全局List <>值上调用Iterable :: spliterator,然后将结果应用于StreamSupport :: stream:

final Spliterator<Integer> spliterator = global.spliterator();

final boolean subSequence = sequence.stream().allMatch(
  itemSequence -> StreamSupport.stream(
    spliterator,
    false
  ).anyMatch(itemSequence::equals)
);

System.out.println(subSequence);

I'll add another option after seeing Holger answer; 看到霍尔格回答后,我会再添加一个选项; but this will only work with jdk-9 Stream.iterate . 但这只适用于jdk-9 Stream.iterate

I've defined a helper method in the same way, a tiny bit different: 我以同样的方式定义了一个辅助方法,有点不同:

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

And then just use an int[2] : 然后只使用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]);

EDIT Holger is right, this will only work for non-duplicates. 编辑 Holger是对的,这只适用于非重复。

I can write it for duplicates also, but then is suffers from the point that fits needs to be called twice now. 我可以写它也重复,但随后从该点受到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();

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

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