15 советов, которые ускорят работу вашего Java-приложения
Ускоряем ваш код, написанный на Java за 15 простых шагов. Советы, которые реально помогут на практике.
1. Избегайте многократного использования условных операторов if-else
Мы используем условные операторы для того, чтобы выполнять какие-либо части кода при соблюдении некоторых условий. Да, это полезный инструмент, но им не стоит злоупотреблять, так как при его многократном использовании, производительность вашей программы будет ухудшаться.
Если в вашей программе слишком много условий, попробуйте сгруппировать их, получить логический результат и использовать их в операторе if.
Кроме того, мы можем подумать об использовании оператора switch вместо нескольких if-else, если это возможно. Оператор switch имеет преимущество в производительности по сравнению с if — else. Ниже приведён пример того, чего следует избегать:
if(condition1){
if (condition2) {
if (condition3 || condition4) { execute ..}
else { execute..}
Лучшим вариантом реализации было бы использовать такой вариант:
логический результат = (условие1 && условие2) && (условие3 || условие4)
@javatg – огромное количество практики Java.
2. Избегайте использования конкатенации строк
Строка – это неизменяемый класс, созданный с помощью типа данных string. Поэтому, если нам нужно создать большую строку, то объединение объектов string с помощью оператора ‘+’ является плохой практикой.
Это приведет к созданию нескольких объектов string, из-за чего начнёт использоваться большое количество памяти.
В этом случае мы можем использовать функции StringBuilder или StringBuffer. Причём первая предпочтительнее второй, поскольку она имеет преимущество в производительности.
Ниже приведён пример того, чего следует избегать:
String query = str1+str2+str3;
Приведённого выше примера стоит избегать. Лучше реализовать это так:
StringBuilder strBuilder = new StringBuilder(“”);
strBuilder.append(str1).append(str2).append(str3);
String query = strBuilder.toString();
3. Избегайте написания длинных методов
Методы не должны быть слишком длинными. Это связано с ухудшением производительности и ухудшением обслуживания кода.
Если методы большие и требуют слишком много обработки, они будут потреблять слишком много памяти.
Попробуйте разбить методы на более мелкие в подходящих по-логике местах.
Я бы посоветовал использовать PMD, Find Bug или плагин Sonar Cube в вашей IDE.
4. Избегайте получения размера коллекции в цикле
При выполнении итераций в любой коллекции, вы сможете узнать её размер перед циклом, но никак не узнаете его во время итерации.
Ниже приведён пример того, чего следует избегать:
List<String> empListData = getEmpData();
for (int i = 0; i < empListData.size(); i++)
{
execute code ..
}
Приведённого выше примера стоит избегать. Лучше реализовать это так:
List<String> empListData= getEmpData();
int size = empListData.size();
for (int i = 0; i < size; i++) {
execute code ..
}
5. Избегайте использования классов BigInteger и BigDecimal
Класс BigDecimal используется тогда, когда нам нужны вещественные числа произвольной длины. Чрезмерное использование этого класса резко снижает производительность, особенно когда он используется для вычисления определенных значений в цикле.
BigInteger и BigDecimal используют много памяти в течение выполнения вычислений.
Если точность не является проблемой или если мы уверены, что диапазон вычисляемого значения не превысит long или double, мы можем избежать использования BigDecimal и вместо этого использовать вышеперечисленные типы данных.
6. Используйте примитивные типы данных везде, где это возможно
Использование примитивного типа данных вместо ссылочных предпочтительнее, поскольку доступ к данным из стековой памяти осуществляется быстрее.
7. Используйте хранимые процедуры вместо запросов
Практика написания хранимых процедур вместо сложных и больших запросов является правильной.
Хранимые процедуры содержатся в базе данных в виде объектов и предварительно компилируются. Время выполнения хранимой процедуры меньше по сравнению с запросом.
8. Избегайте частого создания больших объектов
Существуют определенные классы, которые выступают в качестве владельцев данных в приложении. Эти объекты большие, и их многократного создания следует избегать.
Пример таких объектов – это объекты подключения к базам данных или объекты сеанса для пользователя после входа в систему. При их создании используется много ресурсов.
Нам стоит повторно использовать эти объекты вместо того, чтобы многократно создавать их, поскольку их создание резко снизит производительность приложения из-за увеличения использования памяти.
Мы должны использовать шаблон проектирования “Одиночка” везде, где это возможно, чтобы создать один экземпляр объекта и повторно использовать его везде, где требуется, или клонировать объект вместо создания нового.
9. Используйте методы “contains” с осторожностью в ваших Java-приложениях
Списки, ArrayLists и векторы имеют метод “contains“, который позволяет программистам проверять, есть ли в коллекции уже аналогичный объект. Возможно, вы просматриваете большой список, и очень часто вам может понадобиться найти список уникальных объектов в ней. Ваш код может выглядеть следующим образом:
ArrayList al = new ArrayList();
for (int i=0; i < vars.size(); i++)
{
String obj = (String) vars.get(i);
if (!al.contains(obj))
{
al.add(obj);
}
}
Функционально этот код хорош, но с точки зрения производительности вы проверяете, содержит ли ArrayList объект на каждой итерации цикла. Метод “contains” каждый раз сканирует весь ArrayList. Таким образом, по мере увеличения ArrayList увеличивается снижение производительности.
Вам будет лучше сначала добавить все образцы в ArrayList, один раз выполнить проверку дубликатов, использовать класс HashSet, который по своей сути обеспечивает уникальность, и один раз создать уникальный ArrayList. Вместо того, чтобы иметь потенциально 1000 проверок “contains” в ArrayList, теперь у вас есть одноразовая проверка дубликатов.
ArrayList al = new ArrayList();
…
for (int i=0; i < vars.size(); i++)
{
String obj = (String) vars.get(i);
al.add(obj);
}
al = removeDuplicates(al);
…
static ArrayList removeDuplicates(ArrayList list)
{
if (list == null || list.size() == 0)
{
return list;
}
Set set = new HashSet(list);
list.clear();
list.addAll(set);
return list;
}
10. Использование PreparedStatement вместо Statement
При выполнении SQL-запроса через приложение мы используем JDBC API и классы.
Класс preparedStatement имеет преимущество перед Statement для выполнения запроса, поскольку объект preparedstatement компилируется один раз, а выполняться может несколько раз.
В свою очередь, объект Statement компилируется и выполняется каждый раз, когда его вызывают.
11. Выбирайте только необходимые столбцы в запросах
При получении данных из базы данных мы используем запросы select. Избегайте выбора столбцов, которые не являются необходимыми для дальнейшей обработки.
Выберите только те столбцы, которые нам понадобятся для дальнейшей обработки или отображения на интерфейсе. Выбор слишком большого количества столбцов приводит к задержке выполнения запроса в базе данных.
Избегайте использования “*” при выборе данных из базы данных.
Ниже приведён пример того, чего следует избегать:
select * from employee where emp_id = 100;
Приведённого выше примера стоит избегать. Лучше реализовать это так:
select emp_name, emp_age, emp_gender, emp_occupation, emp_address from employee where emp_id = 100;
12. Использование ненужных инструкция логирования
Логирование является неотъемлемой частью любого приложения и должно быть эффективно реализовано, чтобы избежать снижения производительности.
Мы должны избегать регистрации больших объектов в коде. Логирование должно быть ограничено определенными параметрами.
Кроме того, логирование должно быть сохранено на более высоких уровнях, таких как DEBUG, ERROR, а не INFO. Ниже приведён пример того, чего следует избегать:
Logger.debug("Employee info : " + emp.toString());
Logger.info("Method called for setting employee data:" + emp.getData());
Приведённого выше примера стоит избегать. Лучше реализовать это так:
Logger.debug(“Employee info : ” + emp.getName() + ” : login ID : ” + emp.getLoginId());
Logger.info(“Method called for setting employee data”);
13. Извлечение данных с помощью метода joins
При получении данных из нескольких таблиц, необходимо правильно использовать метод Joins. Если вы реализуете процесс извлечения данных неправильно, это приведёт к снижению производительности приложения.
P.s. Избегайте использования подзапросов, так как это займёт больше времени.
14. Использование EntrySet вместо KeySet
Если вы постоянно итерируете Java-map, то entrySet будет работать лучше, чем keySet, так как entrySet может выполнить на 9000 операций больше, чем keySet за одну секунду.
15. EnumSet – лучший вариант для перечисления значений
Если вы работаете со значениями перечисления, гораздо разумнее использовать EnumSet. Это позволяет выполнять вычисления быстрее, чем при использовании других методов.
Значения EnumSet хранятся в предсказуемом порядке, в то время как другим методом, таким как HashSet, требуется больше времени для получения тех же результатов.