[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()));
Надявам се да ви бъдат полезни някои от тези неща, има и много други, но за тях всеки може да си поекспериментира.
Успех на изпита :)
Не знам дали в тази лекция се спомена или в една друга която гледах, но стана дума за един индиец - Венкат Субраманиам
https://www.youtube.com/watch?v=j9nj5dTo54Q
Също полезно, а и умрях да се смея на някои места хах.