Променливи параметри в Java 8 Streams

Разглеждайки този въпрос: Как да извършвам динамично филтриране в Java 8?

Проблемът е да се съкрати поток след изпълнение на филтър. Не мога да използвам лимит, защото не знам колко дълъг е списъкът след филтъра. И така, можем ли да преброим слементите след филтъра?

Така че реших, че мога да създам клас, който се брои и да прекара потока през карта. 8/22856514#22856514">Кодът е в този отговор.

Създадох клас, който се брои, но оставя елементите непроменени, използвам функция тук, за да избегна използването на ламбда, които използвах в другия отговор:

class DoNothingButCount<T > implements Function<T, T> {
    AtomicInteger i;
    public DoNothingButCount() {
        i = new AtomicInteger(0);
    }
    public T apply(T p) {
        i.incrementAndGet();
        return p;
    }
}

Така че моят поток най-накрая беше:

persons.stream()
    .filter(u -> u.size > 12)
    .filter(u -> u.weitght > 12)
    .map(counter)
    .sorted((p1, p2) -> p1.age - p2.age)
    .collect(Collectors.toList())
    .stream()
    .limit((int) (counter.i.intValue() * 0.5))
    .sorted((p1, p2) -> p2.length - p1.length)
    .limit((int) (counter.i.intValue() * 0.5 * 0.2)).forEach((p) -> System.out.println(p));

Но въпросът ми е за друга част от моя пример.

collect(Collectors.toList()).stream().

Ако премахна този ред, последствията са, че броячът е НУЛА, когато се опитам да изпълня лимит. Някак си мамя изискването "ефективно окончателно", като използвам променлив обект.

Може да греша, но разбирам, че първо се изгражда потокът, така че ако сме използвали променливи обекти, за да предадем параметри на която и да е от стъпките в потока, те ще бъдат взети, когато потокът бъде създаден.

Въпросът ми е, ако предположението ми е правилно, защо е необходимо това? Потокът (ако не е паралелен) може да бъде преминат последователно през всички стъпки (филтър, карта...), така че това ограничение не е необходимо.


person Raul Guiu    schedule 07.04.2014    source източник


Отговори (1)


Кратък отговор

Въпросът ми е, ако предположението ми е правилно, защо е необходимо това? Потокът (ако не е паралелен) може да бъде преминат последователно през всички стъпки (филтър, карта...), така че това ограничение не е необходимо.

Както вече знаете, за паралелни потоци това звучи доста очевидно: това ограничение е необходимо, защото в противен случай резултатът не би бил детерминистичен.

Що се отнася до непаралелните потоци, това не е възможно поради текущия им дизайн: всеки елемент се посещава само веднъж. Ако потоците работеха, както предлагате, те щяха да направят всяка стъпка в цялата колекция, преди да преминат към следващата стъпка, което вероятно би имало въздействие върху производителността според мен. Подозирам, че затова дизайнерите на езика са взели това решение.


Защо технически не работи без collect

Вече знаете това, но ето обяснението за другите читатели. От документите:

Потоците са мързеливи; изчислението върху изходните данни се извършва само когато се стартира операцията на терминала и изходните елементи се консумират само при необходимост.

Всяка междинна операция на Stream, като filter() или limit() всъщност е просто някакъв вид настройка, която инициализира опциите на потока.

Когато извикате терминална операция, като forEach(), collect() или count(), това е моментът, в който се извършва изчислението, като се обработват елементи, следващи изградения преди това конвейер.

Ето защо аргументът на limit() се оценява, преди един елемент да е преминал през първата стъпка от потока. Ето защо трябва да прекратите потока с терминална операция и да започнете нов с limit(), който тогава ще знаете.

По-подробен отговор относно защо да не го разрешите за паралелни потоци

Нека вашият конвейер на потока бъде step X > step Y > step Z.

Искаме паралелно третиране на нашите артикули. Следователно, ако позволим поведението на стъпка Y да зависи от елементите, които вече са преминали през X, тогава Y не е детерминистично. Това е така, защото в момента, в който даден елемент пристигне на стъпка Y, наборът от елементи, които вече са преминали през X, няма да бъде същият при множество изпълнения (поради нишката).

По-подробен отговор относно защо да не го разрешите за непаралелни потоци

По дефиниция потокът се използва за обработка на елементите в поток. Бихте могли да мислите за непаралелен поток по следния начин: един елемент преминава през всички стъпки, след това следващият преминава през всички стъпки и т.н. Всъщност документът казва всичко:

Елементите на потока се посещават само веднъж през живота на потока. Подобно на итератор, трябва да се генерира нов поток, за да се преразгледат същите елементи на източника.

Ако потоците не работят по този начин, няма да е по-добре от това просто да направите всяка стъпка от цялата колекция, преди да преминете към следващата стъпка. Това всъщност би позволило променливи параметри в непаралелни потоци, но вероятно ще има въздействие върху производителността (защото щяхме да повторим няколко пъти колекцията). Както и да е, сегашното им поведение не позволява това, което искате.

person Community    schedule 07.04.2014
comment
Вие казахте: Ако една стъпка в процеса трябва да знае за резултата от обработката на всички елементи в предишните стъпки, тогава идеята за целия поток вече не работи.. Защо? В свързания въпрос потребителят иска да получи най-добрите 50% след прилагане на филтър (така че не можем да използваме ограничение, защото не знаем колко ще бъдат). Защо мислите, че противоречи на идеята за потоци. - person Raul Guiu; 07.04.2014
comment
Защото, IMHO, използването на поток означава, че искате да обработвате всеки елемент по един и същи начин (като в автомобилна фабрика: добавете неща към текущата кола, след което ги предайте на следващата стъпка). Тук, ако трябва да знаете резултат в зависимост от всички елементи, тогава обработката не е еднаква за всеки елемент. Ето защо е необходима терминална операция. - person Joffrey; 07.04.2014
comment
Звучи ми добре. Но все още имаме филтър и лимит, след филтър и лимит някои от елементите няма да бъдат обработени - person Raul Guiu; 07.04.2014
comment
Ами filter филтрира всички елементи по един и същи начин, не е необходимо да знае за всички елементи предварително, а само как изглежда текущият. - person Joffrey; 07.04.2014
comment
Що се отнася до limit, той не зависи от бъдещето, само миналите елементи трябва да бъдат запомнени. - person Joffrey; 07.04.2014
comment
Разгледайте редактираната версия на публикацията ми, може би ще останете по-доволни ;) - person Joffrey; 07.04.2014
comment
Да, разбирам какво имаш предвид сега. +1. Но ще го оставя неприето за известно време, за да дам време на някой да даде по-задълбочен аргумент. - person Raul Guiu; 07.04.2014
comment
Аргументът минало срещу бъдеще не ви убеждава достатъчно? :) - person Joffrey; 07.04.2014