JavaRush /Java блогы /Random-KK /Кофе-брейк №177. Java 8 жүйесіндегі Java ағыны бойынша то...

Кофе-брейк №177. Java 8 жүйесіндегі Java ағыны бойынша толық нұсқаулық

Топта жарияланған
Дереккөз: Hackernoon Бұл пост code мысалдары мен түсіндірмелерімен бірге Java ағынымен жұмыс істеу бойынша егжей-тегжейлі оқулықты ұсынады. Кофе-брейк №177.  Java 8 - 1 жүйесіндегі Java ағыны бойынша толық нұсқаулық

Java 8-де Java Threads-ке кіріспе

Java 8 бөлігі ретінде енгізілген Java Streams деректер жинақтарымен жұмыс істеу үшін пайдаланылады. Олардың өзі деректер құрылымы емес, бірақ түпкілікті нәтиже шығару үшін тапсырыс беру және конвейерлеу арқылы басқа деректер құрылымдарынан ақпаратты енгізу үшін пайдаланылуы мүмкін. Ескертпе: Ағын мен ағынды шатастырмау маңызды, өйткені орыс тілінде екі термин де бір аудармада «ағын» деп аталады. Stream операцияларды орындауға арналған an objectіні (көбінесе деректерді тасымалдау немесе сақтау) білдіреді, ал Thread (сөзбе-сөз аударма - ағын) белгілі бір бағдарлама codeын басқа code тармақтарымен параллель орындауға мүмкіндік беретін an objectіні білдіреді. Stream жеке деректер құрылымы болмағандықтан, ол деректер көзін ешқашан өзгертпейді. Java ағындарының келесі мүмкіндіктері бар:
  1. Java ағынын «java.util.stream» бумасы арқылы пайдалануға болады. Оны codeты пайдаланып сценарийге импорттауға болады:

    import java.util.stream.* ;

    Осы codeты пайдалана отырып, біз Java Stream жүйесінде бірнеше кірістірілген функцияларды оңай іске асыра аламыз.

  2. Java ағыны Java тіліндегі жинақтар мен массивтер сияқты деректер жинақтарынан енгізуді қабылдай алады.

  3. Java Stream кіріс деректер құрылымын өзгертуді қажет етпейді.

  4. Java Stream көзді өзгертпейді. Оның орнына ол сәйкес құбыр жолдарының әдістерін пайдалана отырып, өнім шығарады.

  5. Java ағындары аралық және терминалдық операцияларға жатады, біз оларды келесі бөлімдерде қарастырамыз.

  6. Java ағынында аралық операциялар конвейерленген және жалқау бағалау пішімінде орын алады. Олар терминалдық функциялармен аяқталады. Бұл Java Stream пайдаланудың негізгі пішімін құрайды.

Келесі бөлімде біз Java 8-де қажет болған кезде Java ағынын жасау үшін қолданылатын әртүрлі әдістерді қарастырамыз.

Java 8-де Java Stream құру

Java ағындарын бірнеше жолмен жасауға болады:

1. Stream.empty() әдісі арқылы бос ағын жасау

Кодыңызда кейінірек пайдалану үшін бос ағын жасай аласыз. Stream.empty() әдісін пайдалансаңыз , мәндері жоқ бос ағын жасалады. Бұл бос ағын орындалу уақытында нөлдік көрсеткішті өткізіп жібергіміз келсе, пайдалы болуы мүмкін. Мұны істеу үшін келесі пәрменді пайдалануға болады:
Stream<String> str = Stream.empty();
Жоғарыдағы мәлімдеме ішінде ешқандай элементтері жоқ str деп аталатын бос ағын жасайды . Мұны тексеру үшін str.count() терминін пайдаланып ағынның санын немесе өлшемін тексеріңіз . Мысалы,
System.out.println(str.count());
Нәтижесінде біз шығысында 0 аламыз .

2. Stream.Builder данасы арқылы Stream.builder() әдісін пайдаланып ағын жасаңыз.

Сондай-ақ біз Stream Builder бағдарламасын құрастырушы дизайн үлгісін пайдаланып ағын жасау үшін пайдалана аламыз . Ол an objectілерді кезең-кезеңімен салуға арналған. Stream Builder көмегімен ағынды қалай жасауға болатынын көрейік .
Stream.Builder<Integer> numBuilder = Stream.builder();

numBuilder.add(1).add(2).add( 3);

Stream<Integer> numStream = numBuilder.build();
Бұл codeты пайдаланып, құрамында int элементтері бар numStream атты ағын жасауға болады . Алдымен жасалған numBuilder деп аталатын Stream.Builder данасы арқасында бәрі тез орындалады .

3. Stream.of() әдісін пайдаланып көрсетілген мәндері бар ағын жасаңыз

Ағын жасаудың тағы бір жолы Stream.of() әдісін пайдалануды қамтиды . Бұл берілген мәндері бар ағын жасаудың қарапайым жолы. Ол ағынды жариялайды және инициализациялайды. Ағын жасау үшін Stream.of() әдісін пайдалану мысалы:
Stream<Integer> numStream = Stream.of(1, 2, 3);
Бұл code Stream.Builder арқылы алдыңғы әдісте жасағандай, int элементтері бар ағынды жасайды . Мұнда біз Stream.of() арқылы алдын ала анықталған мәндері бар [1, 2 және 3] ағынын тікелей жасадық .

4. Arrays.stream() әдісін пайдаланып бар массивтен ағын жасаңыз

Жіпті құрудың тағы бір кең таралған әдісі Java-да массивтерді пайдалануды қамтиды. Мұндағы ағын Arrays.stream() әдісі арқылы бар массивтен жасалған . Барлық массив элементтері ағындық элементтерге түрлендіріледі. Міне жақсы мысал:
Integer[] arr = {1, 2, 3, 4, 5};

Stream<Integer> numStream = Arrays.stream(arr);
Бұл code бүтін массив болып табылатын arr деп аталатын массивтің мазмұнын қамтитын numStream жасайды .

5. Stream.concat() әдісі арқылы бар екі ағынды біріктіру

Ағынды жасаудың тағы бір әдісі Stream.concat() әдісі болып табылады . Ол бір жіп жасау үшін екі ағынды біріктіру үшін қолданылады. Екі ағын да ретімен біріктірілген. Бұл бірінші жіптің бірінші, одан кейін екінші жіптің және т.б. Мұндай біріктірудің мысалы келесідей:
Stream<Integer> numStream1 = Stream.of(1, 2, 3, 4, 5);

Stream<Integer> numStream2 = Stream.of(1, 2, 3);

Stream<Integer> combinedStream = Stream.concat( numStream1, numStream2);
Жоғарыдағы мәлімдеме бірінші ағынның numStream1 және екінші ағынының numStream2 элементтерін бір-бірден қамтитын combinedStream деп аталатын соңғы ағынды жасайды .

Java Stream көмегімен операциялар түрлері

Жоғарыда айтылғандай, Java 8-де Java Stream көмегімен екі операция түрін орындауға болады: аралық және терминал. Олардың әрқайсысын толығырақ қарастырайық.

Аралық операциялар

Аралық операциялар шығыс ағынын жасайды және терминал операциясымен кездескен кезде ғана орындалады. Бұл аралық операциялардың жалқаулықпен орындалатынын, конвейерленгенін және тек терминалдық операция арқылы аяқталатынын білдіреді. Жалқау бағалау және конвейерлеу туралы сәл кейінірек білесіз. Аралық операциялардың мысалдары келесі әдістер: filter() , map() , different() , peek() , sorted() және басқалары.

Терминал операциялары

Терминал операциялары аралық операциялардың орындалуын аяқтайды, сонымен қатар шығыс ағынының соңғы нәтижелерін қайтарады. Терминал операциялары жалқау орындалу мен конвейерлеудің аяқталуын білдіретіндіктен, бұл ағынды терминал операциясынан өткеннен кейін қайта пайдалану мүмкін емес. Терминал операцияларының мысалдары келесі әдістер болып табылады: forEach() , collect() , count() , reduce() және т.б.

Java Stream көмегімен операциялардың мысалдары

Аралық операциялар

Мұнда Java ағынына қолдануға болатын кейбір аралық операциялардың мысалдары берілген:

сүзгі()

Бұл әдіс Java тіліндегі белгілі бір предикатқа сәйкес келетін ағынның элементтерін сүзу үшін қолданылады. Бұл сүзгіден өткен элементтер жаңа ағынды құрайды. Жақсырақ түсіну үшін мысалды қарастырайық.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> even = numStream.filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println(even);
Қорытынды:
[98]
Түсініктеме: Бұл мысалда сіз жұп элементтер (2-ге бөлінетін) filter() әдісі арқылы сүзілгенін және мазмұны кейін басып шығарылатын numStream бүтін сандар тізімінде сақталғанын көре аласыз. 98 ағындағы жалғыз жұп бүтін сан болғандықтан, ол шығыста басып шығарылады.

карта()

Бұл әдіс бастапқы кіріс ағынының элементтерінде салыстырылған функцияларды орындау арқылы жаңа ағын жасау үшін пайдаланылады. Жаңа ағынның басқа деректер түрі болуы мүмкін. Мысал келесідей көрінеді:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> d = numStream.map(n -> n*2) .collect(Collectors.toList()); System.out.println(d);
Қорытынды:
[86, 130, 2, 196, 126]
Түсініктеме: Мұнда біз map() әдісі numStream ағынының әрбір элементін екі еселеу үшін қолданылатынын көреміз . Шығарудан көріп отырғаныңыздай, ағындағы элементтердің әрқайсысы сәтті екі еселенген.

бөлек()

Бұл әдіс көшірмелерді сүзу арқылы ағындағы жеке элементтерді ғана алу үшін пайдаланылады. Бірдей мысал келесідей көрінеді:
Stream<Integer> numStream = Stream.of(43,65,1,98,63,63,1); List<Integer> numList = numStream.distinct() .collect(Collectors.toList()); System.out.println(numList);
Қорытынды:
[43, 65, 1, 98, 63]
Түсініктеме: Бұл жағдайда numStream үшін different() әдісі пайдаланылады . Ол ағыннан көшірмелерді жою арқылы numList ішіндегі барлық жеке элементтерді шығарады . Шығарудан көріп отырғаныңыздай, бастапқыда екі көшірме (63 және 1) болған кіріс ағынынан айырмашылығы, көшірмелер жоқ.

қарау()

Бұл әдіс терминал операциясын орындамас бұрын аралық өзгерістерді бақылау үшін қолданылады. Бұл әрі қарай аралық операцияларды орындауға болатын ағын жасау үшін ағынның әрбір элементінде әрекетті орындау үшін peek() функциясын пайдалануға болатынын білдіреді .
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> nList = numStream.map(n -> n*10) .peek(n->System.out.println("Mapped: "+ n)) .collect(Collectors.toList()); System.out.println(nList);
Қорытынды:
Карталанған: 430 Карталанған: 650 Карталанған: 10 Карталанған: 980 Карталанған: 630 [430, 650, 10, 980, 630]
Түсініктеме: Мұнда аралық нәтижелерді жасау үшін peek() әдісі пайдаланылады, себебі map() әдісі ағынның элементтеріне қолданылады. Мұнда біз print операторында тізімнің соңғы мазмұнын басып шығару үшін collect() терминал әрекетін қолданбас бұрын да , әрбір ағындық элементті салыстыру нәтижесі алдын ала дәйекті түрде басып шығарылатынын байқаймыз .

сұрыпталған()

Ағынның элементтерін сұрыптау үшін sorted() әдісі қолданылады. Әдепкі бойынша ол элементтерді өсу ретімен сұрыптайды. Сондай-ақ, параметр ретінде арнайы сұрыптау ретін көрсетуге болады. Бұл әдісті іске асырудың мысалы келесідей көрінеді:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.sorted().forEach(n -> System.out.println(n));
Қорытынды:
1 43 ​​63 65 98
Түсініктеме: Мұнда sorted() әдісі әдепкі бойынша ағынның элементтерін өсу ретімен сұрыптау үшін қолданылады (өйткені нақты тәртіп көрсетілмеген). Шығармада басып шығарылған элементтердің өсу ретімен реттелгенін көруге болады.

Терминал операциялары

Мұнда Java ағындарына қолдануға болатын кейбір терминалдық әрекеттердің мысалдары берілген:

әрқайсысы үшін()

ForEach() әдісі ағынның барлық элементтерін қайталау және функцияны әрбір элементте бір-бірден орындау үшін қолданылады. Бұл for , while және басқалары сияқты цикл операторларына балама ретінде әрекет етеді . Мысалы:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.forEach(n -> System.out.println(n));
Қорытынды:
43 65 1 98 63
Түсініктеме: Мұнда ағынның әрбір элементін бір-бірлеп басып шығару үшін forEach() әдісі қолданылады.

санау()

count() әдісі ағында бар элементтердің жалпы санын шығару үшін пайдаланылады. Ол коллекциядағы элементтердің жалпы санын анықтау үшін жиі қолданылатын size() әдісіне ұқсас . Java ағынымен count() әдісін пайдалану мысалы төмендегідей:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); System.out.println(numStream.count());
Қорытынды:
5
Түсініктеме: numStream құрамында 5 бүтін элемент болғандықтан, ондағы count() әдісін қолдану арқылы 5 шығады.

жинау()

Collection() әдісі ағын элементтерінің өзгермелі қысқартуларын орындау үшін қолданылады. Оны өңдеу аяқталғаннан кейін ағыннан мазмұнды жою үшін пайдалануға болады. Ол қысқартуларды орындау үшін Коллектор класын пайдаланады .
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> odd = numStream.filter(n -> n % 2 == 1) .collect(Collectors.toList()); System.out.println(odd);
Қорытынды:
[43, 65, 1, 63]
Түсініктеме: Бұл мысалда ағындағы барлық тақ элементтер сүзгіден өткізіліп, тақ деп аталатын тізімге жиналады/азайтады . Соңында тақтардың тізімі басып шығарылады.

min() және max()

min() әдісі , аты айтып тұрғандай, ағындағы ең аз элементті табу үшін пайдаланылуы мүмкін. Сол сияқты max() әдісін ағындағы максималды элементті табу үшін пайдалануға болады. Оларды мысалмен қалай қолдануға болатынын түсінуге тырысайық:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); int smallest = numStream.min((m, n) -> Integer.compare(m, n)).get(); System.out.println("Smallest element: " + smallest);
numStream = Stream.of(43, 65, 1, 98, 63); int largest = numStream.max((m, n) -> Integer.compare(m, n)).get(); System.out.println("Largest element: " + largest);
Қорытынды:
Ең кіші элемент: 1 Ең үлкен элемент: 98
Түсініктеме: Бұл мысалда біз numStream ішіндегі ең кіші элементті min () әдісімен және ең үлкен элементті max() әдісімен басып шығардық . Мұнда max() әдісін қолданбас бұрын numStream элементіне қайтадан элементтерді қосқанымызды ескеріңіз . Себебі min() терминалдық операция болып табылады және бастапқы ағынның мазмұнын бұзады, тек соңғы нәтижені қайтарады (бұл жағдайда бүтін сан «ең кіші» болды).

findAny() және findFirst()

findAny() ағынның кез келген элементін Қосымша ретінде қайтарады . Егер ағын бос болса, ол бос болатын Қосымша мәнді де қайтарады . findFirst() ағынның бірінші элементін Қосымша ретінде қайтарады . findAny() әдісі сияқты , findFirst() әдісі де сәйкес ағын бос болса, бос Қосымша параметрді қайтарады . Осы әдістерге негізделген келесі мысалды қарастырайық:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); Optional<Integer> opt = numStream.findFirst();System.out.println(opt); numStream = Stream.empty(); opt = numStream.findAny();System.out.println(opt);
Қорытынды:
Қосымша[43] Қосымша.бос
Түсініктеме: Мұнда бірінші жағдайда findFirst() әдісі ағынның бірінші элементін Қосымша ретінде қайтарады . Содан кейін, ағын бос ағын ретінде қайта тағайындалғанда, findAny() әдісі бос Optional мәнін қайтарады .

allMatch() , anyMatch() және noneMatch()

allMatch() әдісі ағындағы барлық элементтердің белгілі бір предикатқа сәйкестігін тексеру үшін пайдаланылады және егер сәйкес келсе, логикалық мәнді true қайтарады, әйтпесе false мәнін қайтарады . Егер ағын бос болса, ол true мәнін қайтарады . anyMatch() әдісі ағындағы элементтердің кез келгенінің белгілі бір предикатқа сәйкес келетінін тексеру үшін қолданылады. Ол солай болса, шын мәнін , әйтпесе жалған мәнін қайтарады . Егер ағын бос болса, ол false мәнін қайтарады . noneMatch() әдісі егер ағындағы ешбір элемент предикатқа сәйкес келмесе, true мәнін қайтарады , ал басқаша жағдайда false мәнін қайтарады . Мұны көрсету үшін мысал келесідей көрінеді:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); boolean flag = numStream.allMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.anyMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.noneMatch(n -> n==1);System.out.println(flag);
Қорытынды:
жалған шын жалған
Түсініктеме: Элемент ретінде 1 мәні бар numStream ағыны үшін allMatch() әдісі жалған мәнін қайтарады , себебі барлық элементтер 1 емес, тек біреуі ғана. anyMatch() әдісі шын мәнін қайтарады , себебі элементтердің кем дегенде біреуі 1. noneMatch() әдісі жалған мәнін қайтарады , себебі 1 шын мәнінде осы ағында элемент ретінде бар.

Java ағынындағы жалқау бағалаулар

Жалқау бағалау Java 8 жүйесінде Java ағындарымен жұмыс істеу кезінде оңтайландыруларға әкеледі. Олар негізінен терминал операциясы кездескенше аралық операцияларды кейінге қалдыруды қамтиды. Жалқау бағалау нәтиже нақты қажет болғанға дейін есептеулерде ресурстарды қажетсіз ысырап етудің алдын алуға жауап береді. Аралық операциялардың нәтижесінде пайда болатын шығыс ағыны терминал жұмысы аяқталғаннан кейін ғана жасалады. Жалқау бағалау Java ағындарындағы барлық аралық операциялармен жұмыс істейді. Жалқау бағалаудың өте пайдалы қолданылуы шексіз ағындармен жұмыс істегенде орын алады. Осылайша, көптеген қажетсіз өңдеудің алдын алады.

Java ағынындағы құбырлар

Java ағынындағы құбыр желісі кіріс ағынынан, бірінен соң бірі реттелген нөлдік немесе одан да көп аралық операциялардан және соңында терминал операциясынан тұрады. Java ағындарында аралық операциялар жалқау орындалады. Бұл құбырлы аралық операцияларды сөзсіз етеді. Негізінен ретімен біріктірілген аралық операциялар болып табылатын құбыр желілерімен жалқау орындау мүмкін болады. Құбырлар терминал операциясы ақырында кездескеннен кейін орындалуы қажет аралық операцияларды қадағалауға көмектеседі.

Қорытынды

Енді бүгін алған білімімізді қорытындылайық. Бұл мақалада:
  1. Біз Java ағындарының не екенін жылдам қарастырдық.
  2. Содан кейін біз Java 8-де Java ағындарын жасаудың көптеген әртүрлі әдістерін үйрендік.
  3. Біз Java ағындарында орындалатын операциялардың екі негізгі түрін (аралық операциялар және терминалдық операциялар) үйрендік.
  4. Содан кейін біз аралық және терминалдық операциялардың бірнеше мысалдарын егжей-тегжейлі қарастырдық.
  5. Біз жалқау бағалау туралы көбірек білдік және соңында Java ағындарында конвейер құру туралы білдік.
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION