Поток Java 8, как «разбить» в Redu или In Collect без исключения исключения во время выполнения? - PullRequest
0 голосов
/ 29 декабря 2018

Этот вопрос был задан в контексте forEach.

Комментарий (после того, как ответ был принят): Я принял ответ @nullpointer, но он правильный только вв контексте моего примера кода, а не в общем вопросе о разрушающей способности снижения ..

Вопрос:

Но есть ли способ в reduce или в collect преждевременно «сломаться», не пройдя все элементы потока?(Это означает, что мне нужно накапливать состояние во время итерации, поэтому я использую reduce или collect).

Вкратце: мне нужно итерировать все элементы потока (элементы целые и упорядочены от малого к большому), но посмотрите на 2 соседних элемента и сравните их, если разница между ними больше 1, мне нужно «разбить» и остановить «накапливать состояние» и мне нужно вернуть последний переданный элемент.

Вариантбросить RuntimeException и вариант для передачи внешнего состояния - плохо для меня.

Пример кода с комментариями:

public class Solution {

public int solution(int[] A) {

    Supplier<int[]> supplier = new Supplier<int[]>() {
        @Override
        public int[] get() {
            //the array describes the accumulated state:
            //first element in the array , if set > 0, means  - the result is achieved, we can stop iterate over the rest elements
            //second element in the array will represent the "previous element" while iterating the stream
            return new int[]{0, 0};
        }
    };

    //the array in accumulator describes the accumulated state:
    //first element in the array , if set > 0, means  - the result is achieved, we can stop iterate over the rest elements
    //second element in the array will represent the "previous element" while iterating the stream
    ObjIntConsumer<int[]> accumulator = new ObjIntConsumer<int[]>() {
        @Override
        public void accept(int[] sett, int value) {
            if (sett[0] > 0) {
                ;//do nothing, result is set
            } else {
                if (sett[1] > 0) {//previous element exists
                    if (sett[1] + 1 < value) {
                        sett[0] = sett[1] + 1;
                    } else {
                        sett[1] = value;
                    }
                } else {
                    sett[1] = value;
                }
            }
        }
    };

    BiConsumer<int[], int[]> combiner = new BiConsumer<int[], int[]>() {
        @Override
        public void accept(int[] sett1, int[] sett2) {
            System.out.println("Combiner is not used, we are in sequence");
        }
    };

    int result[] = Arrays.stream(A).sorted().filter(value -> value > 0).collect(supplier, accumulator, combiner);
    return result[0];
}


/**
 * We have an input array
 * We need order it, filter out all elements that <=0 (to have only positive)
 * We need find a first minimal integer that does not exist in the array
 * In this example it is 5
 * Because 4,6,16,32,67 positive integers array is having 5 like a minimum that not in the array (between 4 and 6)
 *
 * @param args
 */
public static void main(String[] args) {
    int[] a = new int[]{-2, 4, 6, 16, -7, 0, 0, 0, 32, 67};
    Solution s = new Solution();
    System.out.println("The value is " + s.solution(a));
}

}

Ответы [ 2 ]

0 голосов
/ 29 декабря 2018

Учитывая массив в качестве входных данных, мне кажется, что вы ищете что-то вроде этого:

int stateStream(int[] arr) {
    return IntStream.range(0, arr.length - 1)
            .filter(i -> arr[i + 1] - arr[i] > 1) // your condition
            .mapToObj(i -> arr[i])
            .findFirst() // first such occurrence
            .map(i -> i + 1) // to add 1 to the point where the cehck actually failed
            .orElse(0); // some default value
}

или с нуля, пока вы конвертируете его в отсортированный и отфильтрованный список значений как:

int stateStream(int[] arr) {
    List<Integer> list = Arrays.stream(arr)
            .boxed().sorted()
            .filter(value -> value > 0)
            .collect(Collectors.toList());
    return IntStream.range(0, list.size() - 1)
            .filter(i -> list.get(i + 1) - list.get(i) > 1)
            .mapToObj(list::get)
            .findFirst()
            .map(i -> i + 1)
            .orElse(0);
}
0 голосов
/ 29 декабря 2018

В потоках API нет способов break.Вы можете бросить исключение, но это действительно не очень хорошая идея.Но вы правы - вы можете использовать уменьшить, чтобы найти последний «успешный» элемент в коллекции

Список целых:

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

Позволяет найти значение i-го элемента, где element[i+1]-element[i] > 1:

int result = integers.stream().reduce((i1,i2) -> (i2-i1) > 1 ? i1 : i2).get();

Для этого случая результат будет равен 10. И тогда вы можете просто получить подсписок вашего общего списка;

integers.subList(0,integers.indexOf(result)+1).forEach(s-> System.out.println(s));

Для случаев действительного сбора (когда нет элементов с разницей> 1) result будет равно значению последнего элемента, а подсписок будет равен списку.Таким образом, вы можете добавить некоторые проверки, чтобы избежать .subList, когда это не нужно.

Пример уменьшения:

{1,2,3,5}

Step1:

i1 = 1; i2 = 2; -> reduce(), difference =1, so we reduce this pair to i2 (2)  -> new collection is{2,3,5}

Step2

i1 = 2; i2 = 3; -> reduce(), difference =1, so we reduce this pair to i2 (3)  -> new collection is{3,5}

Step3

i1 = 3; i2 = 5; -> reduce(), difference >1, so we reduce this pair to i1 (3)  -> new collection is {3} and it transforms to Optional<Integer>
...