JavaRush /Java блог /Random /Кофе-брейк #262. Руководство по классу ListUtils и его ос...

Кофе-брейк #262. Руководство по классу ListUtils и его основным методам в Java

Статья из группы Random
Источник: Medium В этом руководстве объясняется предназначение и работа класса ListUtils из библиотеки Apache Commons Collection. Этот класс предоставляет служебные методы и методы-декораторы для интерфейса List в Java. Кофе-брейк #262. Руководство по классу ListUtils и его основным методам в Java - 1Большинству разработчиков знакомы известные классы из пакета java.util, такие как Collections.java и Arrays.java, которые предоставляют встроенные методы для работы с интерфейсом List. Еще одной библиотекой, предоставляющей множество служебных методов через класс Lists.java, является Google Guava.

ListUtils

Проект под названием Apache Commons Collections используется для создания и поддержки группы классов, которые моделируются и мотивируются платформой коллекций Java Development Kit (JDK). Он предлагает широкий спектр классов для решения рутинных задач программирования, связанных с Collection Framework. Класс ListUtils имеет несколько интересных функций для работы с экземплярами List в Java. Чтобы включить библиотеку Apache Commons Collection, добавьте следующую зависимость в файл POM.xml вашего проекта:

<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-collections4</artifactId>
   <version>4.4</version>
 </dependency>
Теперь давайте создадим простой пользовательский объект, который мы будем использовать для проверки функциональности класса ListUtils. Как показано ниже, для создания класса UserObject используются аннотации Lombok:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserObject {
    private String userName;
    private Integer userId;
    private Double userSalary;
}
Затем мы создадим список с именем defaultUserList, который будем использовать при тестировании различных функций класса ListUtils:

List<UserObject> defaultUserList = Arrays.asList(
   new UserObject("User1", 1, 10000.0),
   new UserObject("User2", 2, 20000.0),
   new UserObject("User3", 3, 30000.0),
   new UserObject("User4", 4, 40000.0),
   new UserObject("User5", 5, 50000.0));

Возможности ListUtils

Этот класс предлагает множество встроенных методов для работы с экземплярами List, и вы можете найти о них подробную документацию о них на сайте проекта Apache. Теперь давайте рассмотрим несколько отличительных возможностей методов ListUtils.

1. DefaultIfNull

Этот метод принимает два входных параметра: список по умолчанию (default list) и пользовательский список (user list). Если существующий user list имеет значение NULL, то этот метод возвращает default list. Это удобно при работе со ссылочными данными через экземпляр List, поскольку предотвращает возникновение исключения NullPointerException.

List<UserObject> nullList = null;
assertEquals(defaultUserList,ListUtils.defaultIfNull(defaultUserList, nullList));

2. EmptyIfNull

Этот метод принимает пользовательский список (user list) в качестве входного параметра и возвращает пустой список, если список пользователей имеет значение NULL. Это полезно, когда тип возвращаемого значения метода — List, поскольку он не позволяет нам отправлять нулевой список (null list) в качестве ответа метода.

List<UserObject> nullList = null;
assertTrue(ListUtils.emptyIfNull(nullList).isEmpty());

3. FixedSizeList

Результатом этого метода является список фиксированного размера, поддерживаемый предоставленным исходным списком (input list). Размер этого списка фиксирован, и никакие элементы не могут быть в него добавлены или удалены. Однако элементы списка по-прежнему можно редактировать. Как показано ниже, всякий раз, когда мы пытаемся добавить или удалить элемент из фиксированного списка, он выдает исключение UnsupportedOperationException.

List < UserObject > fixedList = ListUtils.fixedSizeList(defaultUserList);

 assertEquals(Double.valueOf(10000), fixedList.get(0).getUserSalary());

 Exception exception = assertThrows(UnsupportedOperationException.class,
         () -> {
                 fixedList.add(new UserObject("User6", 6, 60000.0));
         });
 assertEquals("List is fixed size", exception.getMessage());

 assertThrows(UnsupportedOperationException.class, () -> {
         fixedList.remove(0);
 });

4. PredicatedList

Этот метод весьма полезен, если мы хотим поместить предикат в качестве проверки в список пользователей. Мы можем использовать его двумя способами. Способ 1. Он предполагает создание списка с пользовательскими данными и последующую его проверку с помощью предиката. Если какой-либо элемент не соответствует условию предиката, выдается ошибка. Это полезно при проверке ввода пользователя. Как показано ниже, ListUtils.predicatedList проверяет, имеют ли все элементы входных списков параметр зарплаты выше 20000,0; в противном случае будет выдано исключение IllegalArgumentException.

assertThrows(IllegalArgumentException.class, () -> {
   ListUtils.predicatedList(defaultUserList,
     userObj -> userObj.getUserSalary() > 20000.0);
  });
Способ 2. Этот подход используется для проверки при вставке данных в список. Введя пустой список и условие предиката, мы можем получить прогнозируемый список. Когда мы пытаемся добавить новую запись данных, она проверяется на соответствие предикату и выдает исключение, если условие не выполняется.

 List<UserObject> predicatedUserList = ListUtils.predicatedList(
    new ArrayList<UserObject>(),
    userObj -> userObj.getUserSalary() > 20000.0);

  assertThrows(IllegalArgumentException.class, () -> {
   predicatedUserList.add(new UserObject("User", 2, 10000.0));
  });

5. Перегородка (Partition)

Этот метод делит список пользователей на подсписки, разделяя его после указания размера подсписка. В приведенном ниже коде показано, как передача размера подсписка, равного 2, приводит к разделению defaultUserList, состоящего из пяти элементов, на три подсписка.

List<List<UserObject>> partitionedList = ListUtils
    .partition(defaultUserList, 2);
  assertEquals(3, partitionedList.size());

6. Index Of

Этот метод принимает на вход (input) список пользователей и предикат. Затем он находит первый элемент, соответствующий условию предиката, и возвращает его индекс.

 int indexOfObject = ListUtils.indexOf(defaultUserList,
    userObj -> userObj.getUserSalary().equals(20000.0));
  assertEquals(1, indexOfObject);

7. Select

Это еще один интригующий метод, который работает так же, как потоковые фильтры Java 8. Он принимает список пользователей и предикат в качестве входных данных и возвращает список параметров, удовлетворяющих условию предиката.

 // Используя java 8 Stream 
 List<UserObject> java8List = defaultUserList
    .stream()
    .filter(userObj -> userObj.getUserSalary() > 20000.0)
    .collect(Collectors.toList());
  assertEquals(3, java8List.size());


// Используя метод ListUtils.select
 List<UserObject> predicatedList = ListUtils.select(defaultUserList,
    userObj -> userObj.getUserSalary() > 20000.0);
  assertEquals(3, predicatedList.size());

8. SelectRejected

Этот метод является обратным ListUtils.select. Мы передаем список пользователей и предикат в качестве входных данных этому методу, и он возвращает список всех элементов, которые не соответствуют критериям предиката.

List<UserObject> listNotMeetingPredicate = ListUtils.selectRejected(
    defaultUserList, userObj -> userObj.getUserSalary() > 20000.0);
  assertEquals(2, listNotMeetingPredicate.size());

9. IsEqualList

Этот метод используется для сравнения двух списков. Он возвращает true тогда и только тогда, когда оба списка имеют одинаковый размер и все соответствующие пары элементов в двух списках равны.

 List<Integer> list1 = Arrays.asList(1, 2, 3, 4);
 List<Integer> list2 = Arrays.asList(1, 2, 3, 4);
 List<Integer> list3 = Arrays.asList(3, 4, 5, 6, 7);
  
 assertTrue(ListUtils.isEqualList(list1, list2));
 assertFalse(ListUtils.isEqualList(list1, list3));
Он также может сравнивать две разные реализации интерфейса List. Чтобы другие типы коллекций могли использовать алгоритм реализации List, метод принимает экземпляры Collection. В следующем примере ArrayList сравнивается с LinkedList и HashSet.

 List<Integer> list1 = Arrays.asList(1, 2, 3, 4);
 List<Integer> linkedList = new LinkedList<>(list1);
 Set<Integer> hashSet = new HashSet<>(list1);
 assertTrue(ListUtils.isEqualList(list1, linkedList));
 assertTrue(ListUtils.isEqualList(list1, hashSet));

10. Перекресток (Intersection)

Этот метод принимает на вход два списка и возвращает новый список, содержащий все элементы, содержащиеся в обоих заданных списках.

 List<Integer> list1 = Arrays.asList(1, 2, 3, 4);
 List<Integer> list3 = Arrays.asList(3, 4, 5, 6, 7);
 
 assertEquals(Arrays.asList(3, 4), ListUtils.intersection(list1, list3));
Обратите внимание, что, помимо вышеперечисленных, существует множество других методов выполнения задач в списках. Существуют методы сложения, вычитания и объединения двух списков. Также существуют методы определения самой длинной общей подпоследовательности двух списков. Если вы хотите ознакомиться с информацией об этих методах, посетите страницу документации на сайте Apache.

11. LazyList

Это еще один интересный метод, предоставляемый классом ListUtils. Он принимает на вход список (list) и преобразователь (transformer) или фабрику (factory), а затем возвращает LazyList, элементы которого создаются по требованию. Способ 1, с использованием преобразователя. Рассмотрим следующий сценарий: мы не хотим, чтобы при получении данных из defaultUserList возникало исключение ArrayIndexOutOfBoundsException. Для этого мы можем использовать LazyList. В следующем примере мы создадим преобразователь (transformer), который проверяет индекс команды поиска. Когда индекс, переданный методу get возвращаемого списка, превышает размер списка, преобразователь используется для создания нового объекта, который затем вставляется по этому индексу. Имейте в виду, что любой индекс, который еще не был получен из LazyList, останется null.

Transformer<Integer, UserObject> transformer = input -> {
   if (defaultUserList.size() > input)
    return defaultUserList.get(input);
   else
    return new UserObject();
  };

  List<UserObject> lazyList = ListUtils
    .lazyList(new ArrayList<UserObject>(),
    transformer);

  assertEquals(defaultUserList.get(0), lazyList.get(0));
  assertEquals(new UserObject(), lazyList.get(6));
  assertEquals(1, lazyList.indexOf(null));
Способ 2, с фабрикой. Используя этот подход, мы можем создать фабрику (factory), которая предоставляет объект пользователя по умолчанию. Теперь мы создаем LazyList, передавая пустой ArrayList и фабрику. Пользователи могут продолжать добавлять данные в этот LazyList; однако, если объект не найден по определенному индексу во время операции извлечения, то фабрика будет использоваться для возврата пользовательского объекта по умолчанию. Этот метод также гарантирует, что операции извлечения никогда не завершатся неудачей. Имейте в виду, что любой индекс, который еще не был получен из LazyList, останется null.

Factory < UserObject > factory = new Factory < UserObject > () {
        public UserObject create() {
                return new UserObject("default", 0, 0.0);
        }
};

List<UserObject> lazyList1 = ListUtils.lazyList(new ArrayList<UserObject>(), factory);
assertEquals("default", lazyList1.get(0).getUserName());
lazyList1.add(0, defaultUserList.get(0));
assertEquals(defaultUserList.get(0), lazyList1.get(0));
assertEquals("default", lazyList1.get(1).getUserName());
Я надеюсь, что, прочитав эту статью, вы будете лучше подготовлены к работе с экземплярами List при программировании на Java. Советую изучить дополнительные возможности библиотеки.
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ