如何将这个迭代代码块转换为流?
Integer prev = -1;
boolean adjacentPair = false;
for(Integer num : numberList) {
if (num.equals(prev)) {
adjacentNumberSame = true;
break;
}
prev = num;
}
return adjacentNumberSame
这是一种(不是很好)的方法:
IntStream.range(1, numberList.size())
.anyMatch(i -> numberList.get(i).equals(numberList.get(i-1)));
如果您的
numberList
是 ArrayList
或类似的东西,那么它的复杂性是可以接受的。如果没有,你最好使用iterator
。像这样:
var Iter = list.stream().iterator();
var res = list.stream()
.skip(1)
.map(v -> v.equals(Iter.next()) )
.anyMatch(v -> v)
;
更优雅的方式需要一些具有
zip
功能的第三方库。使用 zip
它可以看起来像:
var res = zip(
list.stream().skip(1),
list.stream(),
(a, b) -> a.equals(b)
).anyMatch(x->x);
或者您可以编写自己的
zip
。像这样的东西:
public static <A,B,C> Stream<C> zip(Stream<A> listA, Stream<B> listB, BiFunction<A,B,C> zipper){
var iB = listB.iterator();
return listA.filter( a -> iB.hasNext())
.map( a -> zipper.apply(a, iB.next()) );
}
PS。我不会推荐
reduce
,因为它在 @vbezhenar 的答案中使用。因为它不会短路,正如您在这里看到的那样https://repl.it/repls/DisastrousWideRevisioncontrol(寻找异常)
可以使用reduce操作。示例代码:
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 2, 3);
class Result {
boolean adjacentPair = false;
Integer prevItem = null;
}
boolean adjacentPair = list.stream().reduce(new Result(),
(result, item) -> {
result.adjacentPair = result.adjacentPair || item.equals(result.prevItem);
result.prevItem = item;
return result;
},
(result1, result2) -> {
throw new UnsupportedOperationException();
}
).adjacentPair;
System.out.println(adjacentPair);
}
类
Result
保存中间结果。 adjacentPair
字段指示是否遇到相邻对。 prevItem
字段保存在下一次迭代中进行比较的前一项的值。该函数的第三个参数是组合器,它用于并行流,此示例适用于串行流。
流不是正确的出路。这是一个非常笼统的说法。
这里的一个特殊问题是你不能折叠和短路。
我想出的streamy解决方案与@vbezhenar类似,除了它表现良好(有组合器)并且没有引入累加器类。
// Accumulator is: { leftElement, match, rightElement }
// (Could introduce a record.)
// States of the accumulator are:
// { null null null } - initial
// { x null x } - first element
// { x m null } - found match m
// { x null y } - no matches found
Integer[] result = numberList.stream().<Integer[]>collect(
// supplier
() -> new Integer[3],
// accumulator
(acc,t) -> {
if (acc[0] == null) {
// First into acc.
acc[0] = acc[2] = t;
} else if (acc[1] != null) {
// Already found pair to the left.
} else if (t.equals(acc[2])) {
// Found first pair.
acc[1] = t;
acc[2] = null;
} else {
// Otherwise, t replaces rightmost.
acc[2] = t;
}
},
// combiner
(acc, other) -> {
if (acc[1] != null) {
// Alread found pair to the left.
} else if (acc[2] == null) {
// Necessary anyone? Empty acc is combined.
acc[0] = other[0];
acc[1] = other[1];
acc[2] = other[2];
} else if (acc[2].equals(other[0])) {
// Found match.
acc[1] = acc[2];
acc[2] = null;
} else {
// Otherwise, leftmost element with rest as other acc.
acc[1] = other[1];
acc[2] = other[2];
}
}
);
return result[1] != null;
我还没有实际测试过组合器。我怀疑大多数组合器也从未经过测试。
如果我们像 @vbezhenar 那样对流行为做出类似的假设,那么
dropWhile
就是我们的 Johnny 5 朋友。
return numberList.stream().dropWhile(new Predicate<>() {
Integer last;
public boolean test(Integer t) {
Integer lastLast = last;
last = t;
return !t.equals(lastLast);
}
}).findFirst().isPresent();
匿名内部类!!
您不需要做所有这些。为你的方法主体实现这个:
return numberList.contains(prev);
例如,您可以:
public boolean adjacentNumberIsSame(int currentValue, ArrayList<Integer> numberList){
return numberList.contains(currentValue);
}
windowSliding
收集器可用于将流分组为相邻元素对。 然后可以检查每一对,看看两个值是否相同。
boolean adjacentNumberSame = numberList.stream()
.gather(Gatherers.windowSliding(2))
.anyMatch(w -> w.getFirst().equals(w.getLast()));