如何将这个迭代的代码块转换为流呢?
Integer prev = -1;
boolean adjacentPair = false;
for(Integer num : numberList) {
if (num.equals(prev)) {
adjacentNumberSame = true;
break;
}
prev = num;
}
return adjacentNumberSame
如何将这个迭代的代码块转换为流呢?
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()) );
}
提示:我不建议像@vbezhenar的回答中使用reduce
,因为它不能短路,如您可以在这里看到 https://repl.it/repls/DisastrousWideRevisioncontrol(查找异常)
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
字段保存上一次迭代时用于比较的前一个元素值。此函数的第三个参数是 combiner,它用于并行流,但此示例适用于串行流。
return numberList.contains(prev);
public boolean adjacentNumberIsSame(int currentValue, ArrayList<Integer> numberList){
return numberList.contains(currentValue);
}
// 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();
stream
并不总是比for
好。例如,在这里,for
易于理解和直观,而stream
笨重且难以阅读。 - Arnaud Claudel