JavaRush/Java блог/Random/Хватит писать циклы! Топ-10 лучших методов для работы с к...
Джон Дориан
37 уровень

Хватит писать циклы! Топ-10 лучших методов для работы с коллекциями из Java 8

Статья из группы Random
участников
Что такое коллекции и для чего они нужны, думаю, ученикам JavaRush объяснять не надо. Однако после выхода 8-ой версии многие элементарные операции, на которые раньше уходило 6-7 строчек кода, были упрощены до минимума. Без лишних предисловий — топ-10 лучших методов Java8 Collections Framework, которые сэкономят вам кучу времени и места! Хватит писать циклы! Топ-10 лучших методов для работы с коллекциями из Java 8 - 1Всем привет, друзья! Привычка, как известно, вторая натура. И привыкнув писать for (int i = 0; i <......) переучиваться совсем не хочется (тем более, что конструкция эта довольно проста и понятна). Однако, внутри циклов мы часто повторяем одни и те же элементарные операции, от повторения которых очень хотелось бы избавиться. С выходом Java8 Oracle решили помочь нам с этим. Ниже — 10 лучших методов коллекций, которые сэкономят вам кучу времени и кода

1. Iterable.forEach(Consumer<? super T> action)

Название говорит само за себя. Перебирает переданную коллекцию, и выполняет лямбда-выражение action для каждого ее элемента.
List <Integer> numbers = new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7));
numbers.forEach(s -> System.out.print(s + " "));
1 2 3 4 5 6 7

2. Collection.removeIf(Predicate<? super E> filter)

Тоже ничего сложного. Метод перебирает коллекцию, и удаляет те элементы, которые соответствуют filter.
List <Integer> numbers = new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7));
numbers.removeIf(s -> s > 5);
 numbers.forEach(s -> System.out.print(s + " "));
В одну строку удаляем из списка все числа больше 5.

3. Map.forEach(BiConsumer<? super K, ? super V> action)

Метод forEach работает не только для классов, реализующих интерфейс Collection, но и для Map.
Map <String, String> books = new HashMap<>();
books.put("Война и мир", "Лев Толстой");
books.put("Преступление и наказание", "Федор Достоевский");
books.put("Философия Java", "Брюс Эккель");
books.put("Братья Карамазовы", "Федор Достоевский");
books.put("Властелин Колец", "Джон Толкин");
books.forEach((a,b) -> System.out.println("Название книги: " + a + ". Автор: " + b));
Название книги: Братья Карамазовы. Автор: Федор Достоевский
Название книги: Философия Java. Автор: Брюс Эккель
Название книги: Преступление и наказание. Автор: Федор Достоевский
Название книги: Война и мир. Автор: Лев Толстой
Название книги: Властелин Колец. Автор: Джон Толкин

4. Map.compute(K key,BiFunction<? super K, ? super V, ? extends V> remappingFunction)

Выглядит чуть более устрашающе, но на деле прост, как и все предыдущие. Для указанного ключа key этот метод устанавливает в качестве value результат выполнения функции remappingFunction. Например:
Map <String, String> books = new HashMap<>();
books.put("Война и мир", "Лев Толстой");
books.put("Преступление и наказание", "Федор Достоевский");
books.put("Философия Java", "Брюс Эккель");
books.put("Братья Карамазовы", "Федор Достоевский");
books.put("Властелин Колец", "Джон Толкин");
books.forEach((a,b) -> System.out.println("Название книги: " + a + ". Автор: " + b));

books.compute("Философия Java", (a,b) -> b+", крутой чувак");
System.out.println("_______________________");
books.forEach((a,b) -> System.out.println("Название книги: " + a + ". Автор: " + b));
Название книги: Братья Карамазовы. Автор: Федор Достоевский
Название книги: Философия Java. Автор: Брюс Эккель
Название книги: Преступление и наказание. Автор: Федор Достоевский
Название книги: Война и мир. Автор: Лев Толстой
Название книги: Властелин Колец. Автор: Джон Толкин
_______________________
Название книги: Братья Карамазовы. Автор: Федор Достоевский
Название книги: Философия Java. Автор: Брюс Эккель, крутой чувак
Название книги: Преступление и наказание. Автор: Федор Достоевский
Название книги: Война и мир. Автор: Лев Толстой
Название книги: Властелин Колец. Автор: Джон Толкин
Автор "Философии Java" определенно крут!:)

5. Map.computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)

Метод добавит новый элемент в Map, но только в том случае, если элемент с таким ключом там отсутствует. В качестве value ему будет присвоен результат выполнения функции mappingFunction. Если же элемент с таким ключом уже есть — он не будет перезаписан, а останется на месте. Вернемся к нашим книгам и испробуем новый метод:
Map <String, String> books = new HashMap<>();
books.put("Война и мир", "Лев Толстой");
books.put("Преступление и наказание", "Федор Достоевский");
books.put("Философия Java", "Брюс Эккель");
books.put("Братья Карамазовы", "Федор Достоевский");
books.put("Властелин Колец", "Джон Толкин");

books.computeIfAbsent("Гарри Поттер и узник Азкабана", b -> getHarryPotterAuthor());
books.forEach((a,b) -> System.out.println("Название книги: " + a + ". Автор: " + b));
Наша функция mappingFunction:
public static String getHarryPotterAuthor() {
        return "Джоан Роулинг";
    }
А вот и новая книга:
Название книги: Братья Карамазовы. Автор: Федор Достоевский
Название книги: Философия Java. Автор: Брюс Эккель
Название книги: Преступление и наказание. Автор: Федор Достоевский
Название книги: Война и мир. Автор: Лев Толстой
Название книги: Гарри Поттер и узник Азкабана. Автор: Джоан Роулинг
Название книги: Властелин Колец. Автор: Джон Толкин

6. Map.computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

Тот же принцип, что и у Map.compute(), но все вычисления будут выполнены только в случае, если элемент с ключом key уже существует.
Map <String, String> books = new HashMap<>();
books.put("Война и мир", "Лев Толстой");
books.put("Преступление и наказание", "Федор Достоевский");
books.put("Философия Java", "Брюс Эккель");
books.put("Братья Карамазовы", "Федор Достоевский");
books.put("Властелин Колец", "Джон Толкин");

books.computeIfPresent("Евгений Онегин", (a,b) -> b="Александр Пушкин");
System.out.println("_________________");
books.forEach((a,b) -> System.out.println("Название книги: " + a + ". Автор: " + b));
books.computeIfPresent("Братья Карамазовы", (a,b) -> b="Александр Пушкин");
System.out.println("_________________");
books.forEach((a,b) -> System.out.println("Название книги: " + a + ". Автор: " + b));
При первом вызову функции никаких изменений не произошло, потому что книги с названием "Евгений Онегин" в нашей Map нет. А вот во второй раз программа изменила автора для книги "Братья Карамазовы" на "Александр Пушкин". Вывод:
_________________
Название книги: Братья Карамазовы. Автор: Федор Достоевский
Название книги: Философия Java. Автор: Брюс Эккель
Название книги: Преступление и наказание. Автор: Федор Достоевский
Название книги: Война и мир. Автор: Лев Толстой
Название книги: Властелин Колец. Автор: Джон Толкин
_________________
Название книги: Братья Карамазовы. Автор: Александр Пушкин
Название книги: Философия Java. Автор: Брюс Эккель
Название книги: Преступление и наказание. Автор: Федор Достоевский
Название книги: Война и мир. Автор: Лев Толстой
Название книги: Властелин Колец. Автор: Джон Толкин

7. Map.getOrDefault(Object key, V defaultValue)

Возвращает значение, соответствующее ключу key. Если такой ключ не существует — возвращает значение по умолчанию.
Map <String, String> books = new HashMap<>();
books.put("Война и мир", "Лев Толстой");
books.put("Преступление и наказание", "Федор Достоевский");
books.put("Философия Java", "Брюс Эккель");
books.put("Братья Карамазовы", "Федор Достоевский");
books.put("Властелин Колец", "Джон Толкин");

String igor = books.getOrDefault("Слово о полку Игореве", "Неизвестный автор");
System.out.println(igor);
Очень удобно:
Неизвестный автор

8. Map.merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)

Я даже не стал пытаться посчитать, сколько строк кода вам сэкономит этот метод.
  • Если в вашей Map ключ key не существует, или value для этого ключа равно null — метод добавляет в Map переданную пару key-value.
  • Если ключ Key существует и его value != null — метод меняет его value на результат выполнения переданной функции remappingFunction.
  • Если remappingFunction возвращает null - key удаляется из коллекции.
Map <String, String> books = new HashMap<>();
books.put("Война и мир", "Лев Толстой");
books.put("Преступление и наказание", "Федор Достоевский");
books.put("Философия Java", "Брюс Эккель");
books.put("Братья Карамазовы", "Федор Достоевский");
books.put("Властелин Колец", "Джон Толкин");

books.merge("Философия Java", "Брюс Эккель", (a, b) -> b +  " и кто-то там еще");
books.forEach((a,b) -> System.out.println("Название:" + a + ". Автор: " + b));
Вывод:
Название:Братья Карамазовы. Автор: Федор Достоевский
Название:Философия Java. Автор: Брюс Эккель и кто-то там еще
Название:Преступление и наказание. Автор: Федор Достоевский
Название:Война и мир. Автор: Лев Толстой
Название:Властелин Колец. Автор: Джон Толкин
*извини, Брюс*

9. Map.putIfAbsent(K key, V value)

Раньше чтобы добавить пару в Map, если ее там нет, необходимо было делать следующее:
Map <String, String> map = new HashMap<>();
if (map.get("Властелин Колец") == null)
    map.put("Властелин Колец", "Джон Толкин");
Теперь все стало куда проще:
Map<String, String> map = new HashMap<>();
map.putIfAbsent("Властелин Колец", "Джон Толкин");

10. Map.replace и Map.replaceAll()

Последние по списку, но не по значимости. Map.replace(K key, V newValue — заменяет значение ключа key на newValue, если такой ключ существует. Если нет — ничего не происходит. Map.replace(K key, V oldValue, V newValue) — делает то же самое, но только если текущее значение key равно oldValue. Map.replaceAll(BiFunction<? super K, ? super V, ? extends V> function) — заменяет все значения value на результат выполнения функции function. Например:
Map <String, String> books = new HashMap<>();
books.put("Война и мир", "Лев Толстой");
books.put("Преступление и наказание", "Федор Достоевский");
books.put("Философия Java", "Брюс Эккель");
books.put("Братья Карамазовы", "Федор Достоевский");
books.put("Властелин Колец", "Джон Толкин");

books.replace("Братья Карамазовы", "Брюс Эккель", "Джон Толкин");
books.forEach((a,b) -> System.out.println("Название:" + a + ". Автор: " + b));
Название:Братья Карамазовы. Автор: Федор Достоевский
Название:Философия Java. Автор: Брюс Эккель
Название:Преступление и наказание. Автор: Федор Достоевский
Название:Война и мир. Автор: Лев Толстой
Название:Властелин Колец. Автор: Джон Толкин
Не сработало! Текущее значение ключа "Братья Карамазовы" — "Федор Достоевский", а не "Брюс Эккель", поэтому ничего не изменилось.
Map <String, String> books = new HashMap<>();
books.put("Война и мир", "Лев Толстой");
books.put("Преступление и наказание", "Федор Достоевский");
books.put("Философия Java", "Брюс Эккель");
books.put("Братья Карамазовы", "Федор Достоевский");
books.put("Властелин Колец", "Джон Толкин");

books.replaceAll((a,b) -> getCoolWriter());
books.forEach((a,b) -> System.out.println("Название:" + a + ". Автор: " + b));
public static String getCoolWriter() {
        return "Крутой писатель";
    }
Название:Братья Карамазовы. Автор: Крутой писатель
Название:Философия Java. Автор: Крутой писатель
Название:Преступление и наказание. Автор: Крутой писатель
Название:Война и мир. Автор: Крутой писатель
Название:Властелин Колец. Автор: Крутой писатель
Легко изменили значения для всей Map безо всяких сложных конструкций! P.S. Привыкать к новому всегда непросто, но эти изменения действительно хороши. Во всяком случае, некоторые куски моего кода теперь определенно меньше похожи на спагетти, чем раньше:) Если вам понравилась статья, и вы хотели бы увидеть новые - не забудьте поддержать автора в конкурсе, нажав "Нравится", а лучше - "Очень нравится" :) Успехов в обучении!
Комментарии (68)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Максим Li Java Developer
4 декабря 2023, 04:51
Вот это действительно полезная статья!
Alexander Rozenberg
Уровень 32
18 июля 2023, 14:09
fine
Basilius
Уровень 33
5 апреля 2023, 10:32
Оч хорошая статейка, добавил в закладки и прокручу еще раз, когда начну лямбды осваивать. Но, на мой взгляд, если только учитесь, то лучше сначала пользоваться более простыми методами, чтобы понимать как там и что работает. Автор, большое спасибо!
Larandvik
Уровень 30
15 февраля 2023, 18:10
Из прочитанного я понял примерно 10%, я читаю всё, но ни о каких V и T на 14 лвл (или до этого) и речи не шло. Ну может, что-то из этого осядет и в следующий раз будет понятнее. А так для меня это выглядит как справочный материал к которому при необходимости можно или нужно вернуться.
Владоs
Уровень 30
6 марта 2023, 14:25
K - общепринятое обозначение ключа V - общепринятое обозначение значения то есть в HashMap<Integer, String>... Integer будет K (Key), а String будет V (Value) T - тип (Type), то есть сам Класс
Nikita Backend Developer
29 марта 2023, 07:09
Не оспариваю K,V,T, а скорее подмечаю опечатки в описании: Map.replace(K key, V oldValue, V newValue) — делает то же самое, но только если текущее значение key равно oldValue. Видимо имелось ввиду, что находим по key и value, и тогда меняем значение на newValue. Простите если задушнил не много))
Владоs
Уровень 30
4 апреля 2023, 16:46
V просто показывает, что это значение (value), вот и все. оно может не только V, а примеру SomeClass, никто не мешает. просто будет не совсем понятно, верно?
Nikita Backend Developer
5 апреля 2023, 07:14
Конечно, понимаю это и не оспариваю. Видимо не много не точно выразился и наверно не точно написал под Вашими комментариями) Я про то что в статье похоже опечатка в 10-ом пункте - Map.replace и Map.replaceAll(). Map.replace(K key, V oldValue, V newValue) — делает то же самое, но только если текущее значение key равно oldValue. Видимо имелось ввиду, что находим по key и value, и тогда меняем значение на newValue.
Владоs
Уровень 30
5 апреля 2023, 15:47
автор не ошибся, если ты вызовешь метод replace и нажмешь Command + P (не знаю как на виндоус), то увидишь, что у метода две реализации, первая где нужно передать K, V и вторая K, V ,V
Nikita Backend Developer
7 апреля 2023, 07:16
10. Map.replace и Map.replaceAll() Последние по списку, но не по значимости. Map.replace(K key, V newValue) — заменяет значение ключа key на newValue, если такой ключ существует. Если нет — ничего не происходит. Map.replace(K key, V oldValue, V newValue) — делает то же самое, но только если текущее значение key равно oldValue.
Map <String, String> books = new HashMap<>();
books.put("Братья Карамазовы", "Федор Достоевский");
books.replace("Братья Карамазовы", "Брюс Эккель", "Джон Толкин");
books.forEach((a,b) -> System.out.println("Название:" + a + ". Автор: " + b));
Не сработало! Текущее значение ключа "Братья Карамазовы" — "Федор Достоевский", а не "Брюс Эккель", поэтому ничего не изменилось. Фраза "значение key" запутала, воспринял её за сам ключ. В Map.replace(K key, V newValue) фраза сразу понятна была "значение ключа key". Но думаю это мой личный баг в прочтении :) В целом то, статья прекрасна и побольше бы таких! P.S. Тоже Command + P :)
Владоs
Уровень 30
7 апреля 2023, 10:38
еще если заглянуть в документацию oracle интерфейса Map, то там можно увидеть следующее: Документация Map метод replace(K key, V newValue) берет key, находит его value и заменяет его на newValue, после чего возвращает это newValue, что мы передали в параметры. а метод replace(K key, V oldValue, V newValue) тоже берет key, тоже находит его value, но теперь сравнивает с переданным oldValue (ожидаемым value ДО вызова метода). Если реальное значение value ключа key равно переданному значению oldValue, то старое значение заменяется на новое (newValue), и после метод возвратит true или false в зависимости от результата сравнения двух значений. главное понимать что Map это интерфейс и эти методы по сути являются неким контрактом, то есть программист при реализации этого интерфейса должен также реализовать эти методы, как они описаны! такие пироги)
partiec
Уровень 33
5 февраля 2023, 18:53
Статья огонь👍
Denys Sukhoivan
Уровень 30
Expert
4 января 2023, 14:31
Осталось только разобратся как это все работает на практике 😅
IrinaVyu
Уровень 22
19 мая 2023, 13:53
это точно!
Ivan
Уровень 11
22 ноября 2022, 00:30
/* Получите 10 лайков за пост в группе random *\ новичок !может блеснуть умом в подобных темах 😅
Serhii
Уровень 51
1 сентября 2022, 13:55
SWK
Уровень 26
21 июня 2022, 14:39
А вот это в заголовках в скобочках, что за хрень? Типа:
(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
Предполагается, что каждый интеллигентный человек это понимает? Серьёзно?
Илья Городищ
Уровень 21
1 августа 2022, 11:08
Про вопросительные знаки читать https://javarush.com/groups/posts/2324-wildcards-v-generics, вкратце это просто любой тип, лежащий в иерархии выше V в случае super и наследующий V в случае extends соответственно. K и V это не какие-то конкретные классы, это любые типы, что мы и указываем перед BiFunction. От key и value соответственно.
SWK
Уровень 26
1 августа 2022, 14:35
Ты уже обладаешь солидным багажом знаний о них из предыдущих
лекций (об использовании varargs при работе с дженериками и о стирании типов)
Э... А мы к уже, точно, всё просекли про varargs и стирание типов, когда нам вот эту статью подсунули? Что-то мой нездоровый пессимизм подсказывает, что эта полезная лекция откуда-нибудь из блока "Java Collections".
Илья Городищ
Уровень 21
1 августа 2022, 20:54
Про дженерики и стирание типов в Java Syntax рассказывается. А эта фраза просто отсылка к предыдущим статьям, они там блоком лежат. https://javarush.com/groups/posts/2295-chto-takoe-dzheneriki-v-java вот это самая первая статья из блока.
Макс Дудин
Уровень 41
18 августа 2022, 18:04
ага.... примерно оттуда =) не помню уже была ли она, но перскочил судя как раз с последнего Collections
Виталий
Уровень 29
30 октября 2021, 18:43
Просто отлично👍👍. Спасибо за полезную статью!