Как отлаживать проблемы с Java Collections Framework в производственной среде

Как отлаживать проблемы с Java Collections Framework в производственной среде

17 мая 2022 г.

Платформа коллекций Java стала огромным шагом вперед, когда она была представлена ​​как часть Java 2 (JDK 1.2). Благодаря включенным классам коллекций мы, наконец, вышли за пределы «Вектора» и «Хештаблицы» к более зрелым и универсальным решениям. С введением потоков и функциональных концепций в Java 8 фреймворк вывел все на новый уровень.


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


Когда мы отлаживаем типичный класс, мы можем проверять переменные или реализацию. В этом случае набор объектов часто скрыт за абстракцией, которая маскирует сложную внутреннюю структуру, например. красное черное дерево и т. д.


Локальная отладка — это просто


При локальной отладке мы можем просто добавить проверку, такую ​​как aslist.toArray(). Это будет работать плохо, но все равно будет работать. Однако в производственной среде при использовании Lightrun это не удастся. При попытке распечатать сложный список мы можем потерпеть неудачу при вызове самого метода (который может упасть ниже квоты) или просто из-за длины вывода, которая может быть обрезана.


Печать содержимого набора элементов проблематична. Даже если у вас есть код, использующий интерфейс Iterable для циклического перебора всего списка, вероятность избежать ограничений квоты невелика. Печать массива примитивных типов проста, но печать объектов требует большего.


Удаление элементов коллекции


Каркас коллекции включает в себя еще одну проблему при отладке: стирание. В Java можно было бы ожидать, что такой код будет работать:


```java


List myList = new ArrayList<>();


Тогда журнал может выглядеть так:


```java


Значение свойства первого элемента равно {myList.get(0).getProperty()}.


Это не удастся.


Обобщения в Java удаляются во время компиляции и не влияют на байт-код. Таким образом, Lightrun, который работает на уровне байт-кода, не обращает на них внимания. Решение состоит в том, чтобы написать код так, как будто дженерика нет, и привести к соответствующему классу:


```java


Значение свойства первого элемента равно {((MyObject)myList.get(0)).getProperty()}


Обход лимитов квот


Что такое квота?


Lightun выполняет пользовательский код в песочнице. Использование кода будет квалифицироваться как любое условие, журнал выражений и т. д. Песочница позволяет нам гарантировать, что:


  • Код доступен только для чтения и никак не влияет на состояние. Даже если вы вызываете дополнительные методы и т.д.

  • Код не дает сбоев (генерировать исключение и т.д.)

  • Код производительный и не занимает слишком много ресурсов ЦП

У этой песочницы есть свои накладные расходы. Это «лимит квоты» — объем обработки ЦП, выделенный пользовательскому коду. Обратите внимание, что это настраивается для каждого агента отдельно.


Квота может быть затронута, если граф зависимостей объектов является глубоким и требует доступа ко многим объектам класса. ֿОднако есть две вещи, которые мы можем сделать, чтобы извлечь некоторое отлаживаемое значение из интерфейса коллекции.


Использовать снимки


Снимки предоставляют гораздо больше подробностей обо всех типах коллекций. Поскольку они получают доступ к внутреннему состоянию объекта одним выстрелом, они, как правило, захватывают много применимых данных в классе. Например. возьмем, к примеру, этот снимок из демонстрации Spring Boot клиники для домашних животных. В нем перечислены вектор и 10 элементов внутри него. Значения отдельных объектов внутри отчетливо видны на снимке и могут быть легко пройдены.



Использование размера и связанных с ним методов


Отладка — это процесс выдвижения предположений и их проверки. Метод size() из java-коллекций очень эффективен и может использоваться почти свободно. Если вы ожидаете, что результат будет включать фиксированный набор элементов, вы можете легко использовать методы size() или isEmpty(), чтобы указать, соответствует ли коллекция ожиданиям. Вызов метода здесь будет очень эффективным.


Вы можете использовать его как условие или в самом формате журнала:



Регистрация отдельной записи


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


В приведенном ниже коде для скрытия элементов используется API потоков Java. В этот код преобразования я могу вставить журнал и распечатать его только в том случае, если ветеринаром являюсь я. Это условие, которое использует метод getFirstName() класса Vet:


```java


vet.getFirstName().equals("Шай")


Если это соблюдено, я могу распечатать полную информацию для записи: «Текущий ветеринар {newVet}».



Подготовка


Отладка коллекций Java сложнее, если мы не готовы. Приятно то, что подготовка также является первым шагом в написании лучшего кода для долгосрочного сопровождения. Это применимо ко всем видам коллекций, а также хорошо работает для коллекций и потоковых операций.


Самая большая ошибка на сегодняшний день — это слишком лаконичный код. Я и здесь виноват… этот код возвращается непосредственно из метода:


```java


вернуть vets.findAllByOrderById(Pageable.ofSize(5).withPage(страница)).stream().map(vet -> {


VetDTO newVet = новый VetDTO();


новыйVet.setId(vet.getId());


newVet.setLastName(vet.getLastName());


newVet.setFirstName(vet.getFirstName());


Set pets = findPetDTOSet(vet.getId());


новыйVet.setPets(домашние животные);


вернуть новыйВет;


}).collect(Коллекторы.toList());


Это кажется намного круче, чем этот код, который возвращается из метода после присвоения значения:


```java


List returnValue = vets.findAllByOrderById(Pageable.ofSize(5).withPage(страница)).stream().map(vet -> {


VetDTO newVet = новый VetDTO();


новыйVet.setId(vet.getId());


newVet.setLastName(vet.getLastName());


newVet.setFirstName(vet.getFirstName());


Set pets = findPetDTOSet(vet.getId());


новыйVet.setPets(домашние животные);


вернуть новыйВет;


}).collect(Коллекторы.toList());


вернуть возвращаемое значение;


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


Это особенно верно при работе с потоками Java, которые подчеркивают такой лаконичный синтаксис.


Включите правильные методы toString


Я не могу не подчеркнуть этого достаточно: если он входит в структуру коллекции, он должен иметь в классе метод toString(). Это значительно упрощает отладку элементов!


Когда мы включаем класс в снимок или журнал, вызывается метод toString(). Если в классе нет реализации, мы увидим идентификатор объекта, который не так полезен.


Резюме


Снимки лучше подходят для отладки объектов инфраструктуры коллекций, поскольку они отображают больше иерархии. Потоки Java можно отлаживать, но из-за их лаконичности по умолчанию они более сложны. Мы должны попытаться написать код, который не будет таким кратким как для упрощения регистрации, так и для отладки.


Печать всего в интерфейсе Iterable не будет работать, но использование условного оператора для печати только важной строки может работать довольно хорошо.


Стандартные методы в коллекции могут оказаться слишком дорогими для механизма квотирования процессорного времени. Но такие API, как isEmpty() или \size()`, эффективны.


Также опубликовано здесь



Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE