Loading...
LoKSET avatar LoKSET 160 Точки

[Useful Info]Streams and Lambdas in Java

Здравейте колеги, след преминаването към Джава в последния месец остро усетих липсата на някои неща с които бях свикнал в C#. Макар и индексаторите, операторите и пропъртитата да няма как да се въведат, оказа се че мисленето ми че няма нищо подобно на LINQ в Java е било доста погрешно. Някои от вас сигурно са се запознали с новостите в Java8, но за тези които не са ще опиша някои неща, които доста се доближават до безкрайното удобство и user-friendliness на LINQ. Естествено това е Джава и поне за мен те са въведени по малко дървен начин, но мисля това може да се очаква като се има предвид езика :D

За да се ползват тези благинки трябва да работим с Колекции, т.е. не може да се прилагат върху масиви например, ако първо не ги превърнем в колекция. Нека в общия случай работим с List. Някои общи паралели (не винаги са 1:1, но за простота ще ги опиша така)- Where() в C# == filter() в Java, Select() == map(), ToList()==collect(Collectors.toList())

За да се ползват тези функции, колекцията трябва да се превърне в stream -нещо подобно на IEnumerable в C#, наред със lazy evaluation-a и директното връщане на обработената стойност, така че да могат да се чейнват функции една след друга.

Един прост пример - имаме

List<Integer> numbers = Arrays.asList(1 ,7 ,252, -12, 50);

Ако искаме да извадим четните числа ползваме

List<Integer> filtered = numbers.stream().filter(x -> x % 2 ==0).collect(Collectors.toList());

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

--

Ако искаме average, min, max или друго подобно трябва да ползваме компаратор, за съжаление това е направено доста по-глупаво от еквивалентите в C# например, освен това има и различни начини да се разпише (get служи за да се вземе самото число, защото.... защо пък да го върне дирекно):

int max = numbers.stream().max(Comparator.<Integer>naturalOrder()).get();
int max = numbers.stream().max(Comparator.comparingInt(i->i)).get();
int max = numbers.stream().mapToInt(i->i).min().getAsInt(); - тук първо уточняваме че са интове и тогава директно може да се извика min, което обаче връща OptionalInt и затова му извикваме getAsInt()
int max = numbers.stream().max(Integer::compare).get();

Кое трябва да се ползва? - по-скоро последното, но щом всички работят... вие си решавате.

--

count може да се извиква директно на стрийма =>

long count = numbers.stream().count();

С map можем да трансформираме началния лист, например искаме да увеличим всички числа с 10, точно като при Select:

List<Integer> mapped = numbers.stream().map(i -> i+10).collect(Collectors.toList());

С map може да се прави и парсването на един ред, което обичаме от C#:

String input = "1 7 252 -12 50";
List<Integer> parsed = Arrays.asList(input.split("\\s+")).stream().map(Integer::parseInt).collect(Collectors.toList());

Както се вижда двойното двоеточие се ползва за извикване на метод от съответния клас(без да се подават никакви параметри) - съответно

map(Integer::parseInt) == map(i -> Integer.parseInt(i))

--

Сортиране - за него има и други варианти естествено - през Arrays и Collections, но може и така, ако искаме да навържем няколко неща =>

List<Integer> numbers = Arrays.asList(1 ,7 ,252, -12, 50);

List<Integer> filtered = numbers.stream().sorted().collect(Collectors.toList());

В sorted може да се ползват и други компаратори - ако искаме обратен ред например ползваме

sorted(Comparator.<Integer>reverseOrder())

--

Ако искаме да направим стринг, подобно на String.join има друг колектор, който може да се използва (тук правим и обратен ред на стринговете) =>

List<String> strings = Arrays.asList("abc", "ljasd", "qweqw", "zaeasea", "popop");
String joined = strings.stream().sorted(Comparator.<String>reverseOrder()).collect(Collectors.joining(", ")); - сортираме по обратен ред и съединяваме със запетая и спейс.

--

Има и нещо подобно на Enumerable.Range oт C# - IntStream - например искаме всички четни числа от 1 до 100 ->

List evenNumbers= IntStream.range(1,51).map(i -> i * 2).boxed().collect(Collectors.toList());

Тук boxed се използва за да се премине от IntStream към Stream, тъй като първото не може да се колектне в Лист.

--

forEach - това е нещо, което в Java e реализирано по-добре в сравнение със C#, тъй като там е дефиниран такъв метод само за листове, а е пропуснат при IEnumerable поради "философски" причини.

List<Integer> numbers = Arrays.asList(1 ,7 ,252, -12, 50);

numbers.stream().sorted().forEach(System.out::println);

Сортираме числата и после ги принтираме на всеки ред.

--

И накрая groupBy, което може да ни потрябва за някои задачи на изпита ->

имаме студенти, които искаме да сортираме по име и после  групираме по определено пропърти - отново се работи с колектори и се връща Map

Map<Integer, List<Student>> groupedStudents = students.stream().sorted(Comparator.comparing(Student::getName)).collect(Collectors.groupingBy(s -> s.getScore()));

Надявам се да ви бъдат полезни някои от тези неща, има и много други, но за тях всеки може да си поекспериментира.

Успех на изпита :)

Тагове:
14
Java Advanced 02/10/2015 23:28:45
ivailozd avatar ivailozd 75 Точки

Здравей,

Поздравления за темата, искам да добавя линк към една лекция, която много добре обяснява защо нещата се случват по този начин в Java: ТУК.

В нея се обяснява и какво е default method в interface и защо е добавена подобна функционалност.

0
LoKSET avatar LoKSET 160 Точки

Не знам дали в тази лекция се спомена или в една друга която гледах, но стана дума за един индиец - Венкат Субраманиам

https://www.youtube.com/watch?v=j9nj5dTo54Q

Също полезно, а и умрях да се смея на някои места хах.

0
03/10/2015 22:15:36
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.