JavaRush /Java блогу /Random-KY /Кофе-брейк №177. Java 8деги Java агымы боюнча толук колдо...

Кофе-брейк №177. Java 8деги Java агымы боюнча толук колдонмо

Группада жарыяланган
Булак: Hackernoon Бул постто Java Stream менен иштөө боюнча кеңири окуу куралы, ошондой эле code мисалдары жана түшүндүрмөлөрү камтылган. Кофе-брейк №177.  Java 8 - 1деги Java агымы боюнча толук колдонмо

Java 8деги Java Threads менен таанышуу

Java 8дин бир бөлүгү катары киргизилген Java Streams маалымат жыйнагы менен иштөө үчүн колдонулат. Алар өзүлөрү маалымат структурасы эмес, бирок акыркы натыйжаны алуу үчүн буйрутма жана конвейер аркылуу башка маалымат структураларынан маалыматты киргизүү үчүн колдонулушу мүмкүн. Эскертүү: Агым менен Threadди чаташтырбоо маанилүү, анткени орус тorнде эки термин тең бир эле котормодо “агым” деп аталат. Stream операцияларды аткаруу үчүн an objectти билдирет (көбүнчө берorштерди өткөрүү же сактоо), ал эми Thread (сөзмө-сөз которуу - жип) белгилүү бир программалык codeду башка code бутактары менен параллелдүү аткарууга мүмкүндүк берүүчү an objectти билдирет. Stream өзүнчө маалымат түзүмү болбогондуктан, ал эч качан маалымат булагын өзгөртпөйт. Java агымдары төмөнкү өзгөчөлүктөргө ээ:
  1. Java Stream "java.util.stream" пакетин колдонуу менен колдонсо болот. Аны codeду колдонуп скриптке импорттоого болот:

    import java.util.stream.* ;

    Бул codeду колдонуу менен биз Java Streamде бир нече орнотулган функцияларды оңой эле ишке ашыра алабыз.

  2. Java Stream Javaдагы коллекциялар жана массивдер сыяктуу маалымат жыйнактарынан киргизүүнү кабыл алат.

  3. Java Stream киргизүү маалымат түзүмүн өзгөртүүнү талап кылbyte.

  4. Java Stream булагын өзгөртпөйт. Анын ордуна, ал ылайыктуу түтүк ыкмаларын колдонуу менен өндүрүштү түзөт.

  5. Java агымдары аралык жана терминалдык операцияларга дуушар болушат, аларды биз кийинки бөлүмдөрдө талкуулайбыз.

  6. Java Stream'де аралык операциялар өткөрүлөт жана жалкоо баалоо форматында ишке ашат. Алар терминалдык функциялар менен аяктайт. Бул Java Streamди колдонуу үчүн негизги форматты түзөт.

Кийинки бөлүмдө биз Java 8де Java агымын түзүү үчүн колдонулган ар кандай ыкмаларды карап чыгабыз.

Java 8де Java агымын түзүү

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 агымына колдонула турган кээ бир ортодогу операциялардын айрым мисалдары келтирилген:

filter()

Бул ыкма 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 агымынын ар бир элементин эки эсеге көбөйтүү үчүн колдонулганын көрөбүз . Чыгуудан көрүнүп тургандай, агымдагы элементтердин ар бири ийгorктүү эки эселенген.

distinct()

Бул ыкма дубликаттарды чыпкалоо аркылуу агымдагы айрым элементтерди гана алуу үчүн колдонулат. Ошол эле мисал мындай көрүнөт:
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()

Бул ыкма терминалдык операцияны аткаруудан мурун аралык өзгөрүүлөргө көз салуу үчүн колдонулат. Бул агымдын ар бир элементи боюнча операцияны аткаруу үчүн 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]
Түшүндүрмө: Бул жерде агымдын элементтерине map() ыкмасы колдонулгандыктан, аралык натыйжаларды түзүү үчүн peek() ыкмасы колдонулат. Бул жерден биз 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()

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

чогултуу()

collect() методу агым элементтеринин өзгөрүлмө кыскартууларын аткаруу үчүн колдонулат. Аны иштетип бүткөндөн кийин агымдан мазмунду алып салуу үчүн колдонсо болот. Ал кыскартууларды аткаруу үчүн Коллектор классын колдонот .
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]
Түшүндүрмө: Бул мисалда агымдагы бардык так элементтер чыпкаланган жана odd деп аталган тизмеге чогултулган/кичирейген . Аягында кызыктардын тизмеси басылып чыгат.

min() жана макс()

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] Optional.empty
Түшүндүрмө: Бул жерде, биринчи учурда, findFirst() ыкмасы агымдын биринчи элементин Кошумча катары кайтарат . Андан кийин, жип бош жип катары дайындалганда, findAny() ыкмасы бош Optional кайтарат .

allMatch() , anyMatch() жана noneMatch()

allMatch() методу агымдагы бардык элементтердин белгилүү бир предикатка дал келгендигин текшерүү үчүн колдонулат жана эгер туура келсе логикалык маанини true кайтарат, антпесе false кайтарат . Эгерде агым бош болсо, анда ал чындыкты кайтарат . anyMatch() методу агымдагы элементтердин кайсынысы болбосун белгилүү бир предикатка дал келгенин текшерүү үчүн колдонулат. Ал эгер чын болсо, анда жалган деп кайтарат . Эгерде агым бош болсо, ал false кайтарат . noneMatch() методу агымдагы эч бир элемент предикатка дал келбесе чындыкты кайтарат , ал эми башка учурда 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 Streams менен иштөөдө оптималдаштырууга алып келет. Алар негизинен терминалдык операция жолукканга чейин аралык операцияларды кечиктирүүнү камтыйт. Жалкоо баалоо натыйжа чындап керек болгонго чейин эсептөөлөр боюнча ресурстардын ашыкча ысырап болушуна жол бербөө үчүн жооп берет. Аралык операциялардан келип чыккан чыгаруу агымы терминалдык операция аяктагандан кийин гана түзүлөт. Lazy баалоо Java агымындагы бардык орто операциялар менен иштейт. Жалкоо баалоону абдан пайдалуу колдонуу чексиз агымдар менен иштөөдө пайда болот. Бул жол менен, көп керексиз иштетүүнүн алдын алат.

Java агымындагы түтүктөр

Java агымындагы куур биринин артынан бири тизилген кириш агымынан, нөл же андан көп аралык операциялардан жана акырында терминалдык операциядан турат. Java агымдарында аралык операциялар жалкоолук менен аткарылат. Бул түтүкчөлөр аралык операцияларды сөзсүз кылат. Негизинен ирети менен бириктирилген аралык операциялар болгон түтүк өткөргүчтөрү менен жалкоолук менен аткаруу мүмкүн болот. Түтүк өткөргүчтөр терминалдык операция жолуккандан кийин аткарылышы керек болгон аралык операцияларды көзөмөлдөөгө жардам берет.

Корутунду

Эми бүгүн эмнени үйрөнгөнүбүздү жыйынтыктайлы. Бул макалада:
  1. Биз Java Streams деген эмне экенин тез карап чыктык.
  2. Андан кийин биз Java 8де Java жиптерин түзүүнүн көптөгөн ар кандай ыкмаларын үйрөндүк.
  3. Биз Java агымдарында аткарыла турган операциялардын эки негизги түрүн (аралык операциялар жана терминалдык операциялар) үйрөндүк.
  4. Андан кийин биз орто жана терминалдык операциялардын бир нече мисалдарын деталдуу карап чыктык.
  5. Биз жалкоо баалоо жөнүндө көбүрөөк билдик жана акырында Java жиптериндеги куурларды үйрөндүк.
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION