java потоков: самый простой способ найти соседнее значение соответствия - PullRequest
3 голосов
/ 16 февраля 2020

Как можно было бы преобразовать этот итеративный блок кода в потоки?

Integer prev = -1;
boolean adjacentPair = false;

for(Integer num : numberList) {
    if (num.equals(prev)) {
        adjacentNumberSame = true;
        break;
    }
    prev = num;
}
return adjacentNumberSame 

Ответы [ 4 ]

4 голосов
/ 17 февраля 2020

Вы можете использовать операцию уменьшения. Пример кода:

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

class Result содержит промежуточный результат. Поле adjacentPair указывает, встречалась ли соседняя пара. Поле prevItem содержит значение предыдущего элемента, которое сравнивается на следующей итерации. Третий аргумент этой функции - комбинатор, он используется для параллельных потоков, и этот пример работает с последовательными потоками.

3 голосов
/ 17 февраля 2020

Это один (не очень хороший) способ сделать это:

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 (ищите исключение)

1 голос
/ 18 февраля 2020

Потоки - это не путь к go. Это довольно общее утверждение.

Особая проблема здесь в том, что вы не можете сгибать и замыкать .

потоковый решение , которое я придумал, было похоже на @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 - наш друг Джонни 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();

Анонимные внутренние классы ftw !!

1 голос
/ 16 февраля 2020

Вам не нужно делать все это. Реализуйте это для тела вашего метода:

return numberList.contains(prev);

Например, вы можете иметь:

public boolean adjacentNumberIsSame(int currentValue, ArrayList<Integer> numberList){
    return numberList.contains(currentValue);
}
...