Java Learning
18.6K subscribers
1.62K photos
1 video
3 files
1K links
№ 5079899194

Обучающий канал по Java

Ссылка для друга - https://t.iss.one/+ZEYYht6-46w5MDM6

По всем вопросам @mascarov_valentin

Реклама на бирже - https://telega.in/c/Java_per_month
Download Telegram
Ответ:
Anonymous Quiz
6%
5
76%
10
4%
true
14%
false
👍101
👀 Map.computeIfAbsent()/getOrDefault()/merge()/putIfAbsent()

Возможно, если вы пишете на Java, то у вас в проекте есть код похожий на этот:

Map<String, Integer> data = ...;
for (String s : strings) {
if (!data.containsKey(key))
data.put(key, 0);
data.put(key, data.get(key) + 1);
}


Суть проста. Есть отображение из строки в счетчик, сколько раз мы встретили эту строку. Надо только не забывать инициализировать позиции Map‘а нулем, а то виртуальная машина в вас NullPointerException кинет.

➡️ В Java 8 эта же задача решается проще:

for (String s : strings)
data.merge(s, 1, (a, b) -> a + b);


Meтод merge принимает ключ, значение и функцию которая объединяет заданное значение и уже существующее в отображении, если таковое имеется. Если в отображении под заданным ключем значения нет, то кладет туда указанное значение.

✔️ Аналогичную функциональность, но в другом контексте, дают методы:

computeIfAbsent() – возвращает или значение из отображения по ключу, или создает его, если его не было;

cputIfAbsent() – добавляет значение в отображение, только если его там не было. Этот метод ранее имелся только у ConcurrentMap, теперь появился и у Map‘а;

cgetOrDefault() – название довольно красноречиво. Возвращает значение из отображения или переданное значение по-умолчанию. На мой взгляд, метод довольно не идиоматичен. Для работы с отсутствующими значениями был добавлен тип Optional, его и следовало использовать. Поэтому, я бы добавил метод: Optional<V> getOptional(K key).

Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍91
➡️ ThreadLocal.withInitial()

Тех, кто плотно работает с многопоточностью, ничем не пронять. Они как ветераны Вьетнама, и даже флешбеки по ночам так же мучают. И этой конструкцией их не напугаешь:

// Java 7 и ранее
ThreadLocal<ObjectMapper> mapper = new ThreadLocal<>() {
@Override
protected ObjectMapper initialValue() {
return new ObjectMapper();
}
};


✔️ Но теперь, за счёт замыканий, стало проще:

// Java 8
ThreadLocal<ObjectMapper> mapper = withInitial(() -> new ObjectMapper());


Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
⚙️ Files.lines()/readAllLines()/BufferedReader.lines()

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

🗣️ Аналогичный метод был добавлен в класс BufferedReader, поэтому теперь Stream’ы доступны поверх любого InputStream‘а.

Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Парадокс Comparator’а

Если вам нужно написать Comparator для сортировки объектов по возрастанию, обычно он выглядит так:

public class ByScoreComparator implements Comparator<User> {

@Override
public int compare(User u1, User u2) {
return (int) signum(u2.getAge() - u1.getAge());
}
}


➡️ Главная проблема — понять, от чего отнимать, чтобы порядок был правильным. Правильный ответ с первого раза получается редко, и часто мы замечаем ошибку уже после запуска кода.

Теперь это не нужно. Можно использовать ссылки на методы:

Comparator<User> comparator = Comparator
.comparingDouble(User::getAge)
.thenComparing(User::getName);
List<User> hList = ...;
hList.sort(comparator);


🗣️ Такой подход упрощает создание компараторов.

Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22
⚙️ Instancio

Как вы генерируете тестовые данные в ваших модульных тестах? Instancio поможет нам в этом.

Ее цель — сократить время и количество строк кода, затрачиваемых на ручную настройку данных в модульных тестах. Она создает объекты и заполняет их случайными данными, делая наши тесты более динамичными.

✔️ С помощью Instancio мы можем генерировать случайные данные, но в то же время мы можем установить пользовательские данные в определенном поле.

🔗 Ссылочка на доку

Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7😢1
👀 Datafaker

Datafaker создает фиктивные данные для ваших программ JVM за считанные минуты, используя широкий спектр из более чем 100 поставщиков данных.

🗣️ Это может быть очень полезно при генерации тестовых данных для заполнения базы данных, генерации данных для стресс‑теста или анонимизации данных из производственных сервисов. Давайте включим его в наши зависимости.

🔗 Ссылочка на доку

Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Что будет выведено при выполнении кода?

Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
⚙️ LongAccumulator/LongAdder

Два класса, которые представляют собой более производительные замены для AtomicLong. Класс LongAdder позволяет выполнять атомарные арифметические операции над типом long. LongAccumulator принимает произвольную функцию аккумуляции результатов.

🗣️ Эта функция принимает текущее значение, аргумент переданный в метод accumulate() и возвращает результат логического объединения (accumulate) двух значений.

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


Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
ℹ️ Использование entrySet для итерации по HashMap

Раньше я использовал keySet для итерации по HashMap, как показано ниже:

Set<Key> keySet = map.keySet();

for (Key k : keySet) {
Value v = map.get(k);
print(k, v);
}


➡️ Это выполняет дополнительный поиск для получения значения из Map, что в худшем случае может быть O(n). Если вам нужны и ключ, и значение, то лучше итерировать по entrySet, а не по keySet.

Set<Map.Entry<Key, Value>> entrySet = map.entrySet();

for (Map.Entry<Key, Value> e : entrySet) {
Key k = e.getKey();
Value v = e.getValue();
}


✔️ Это более эффективно, потому что вы получаете значение напрямую из объекта, что всегда занимает O(1).

Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍201
⚙️ Использование Enum как Singleton

Жаль, что я не знал раньше, что можно написать Singleton в Java всего одной строкой:

public enum Singleton {
INSTANCE;
}


✔️ Это потокобезопасно, надежно, и Java гарантирует наличие только одного экземпляра, даже в случае сериализации и десериализации.

➡️ Использование перечислений (enum) для реализации паттерна Singleton является отличным подходом в Java. Enum обеспечивает множество преимуществ:

Потокобезопасность: Перечисления в Java создаются с использованием потокобезопасного механизма. Это означает, что вы не столкнетесь с проблемами, связанными с многопоточностью.

Гарантия единственности экземпляра: Java гарантирует, что в системе будет только один экземпляр перечисления, даже при использовании различных потоков или классов загрузчиков.

Сериализация и десериализация: При использовании enum Java автоматически обрабатывает сериализацию и десериализацию таким образом, чтобы сохранить единственность экземпляра. Это означает, что при десериализации вы не получите новый экземпляр, а вернется тот же самый экземпляр.

Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17
👀 Использование Arrays.asList() для инициализации коллекций или List.of(), Set.of()

Раньше, даже зная элементы заранее, я инициализировал коллекцию так:

List<String> listOfCurrencies = new ArrayList<>();
listOfCurrencies.add("USD/AUD");
listOfCurrencies.add("USD/JPY");
listOfCurrencies.add("USD/INR");


🗣️ Хотя Arrays.asList() возвращает List, его нужно передать конструктору ArrayList, потому что возвращаемый список имеет фиксированную длину, и вы не можете добавлять или удалять элементы.

✔️ Начиная с Java 9, можно использовать методы List.of() и Set.of() для создания списка и множества с элементами. Это предпочтительнее, так как они возвращают неизменяемые списки и множества.

Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17
👀 Проверка состояния wait() в цикле

Когда я только начинал писать код для межпоточной коммуникации с использованием методов wait(), notify() и notifyAll(), я использовал if для проверки условия ожидания перед вызовом wait() и notify():

synchronized(queue) {
if(queue.isFull()){
queue.wait();
}
}


🗣️ К счастью, проблем не возникло, но я понял свою ошибку, прочитав раздел из книги Effective Java. Там сказано, что условие ожидания следует проверять в цикле, потому что потоки могут получать ложные уведомления, и до того, как вы что-то сделаете, условие может снова стать истинным. Поэтому правильный способ использования wait() и notify() выглядит так:


synchronized(queue) {
while(queue.isFull()){
queue.wait();
}
}


Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14
➡️ Обработка CloneNotSupportedException и возврат экземпляра подкласса

Несмотря на критику функции клонирования объектов в Java, если вам нужно реализовать метод clone(), вот несколько лучших практик для упрощения задачи:

public Course clone() {
Course c = null;
try {
c = (Course)super.clone();
} catch (CloneNotSupportedException e) {} // Не произойдет

return c;
}


🗣️ Этот код использует тот факт, что метод clone() не вызовет CloneNotSupportedException, если класс реализует интерфейс Cloneable. Возврат подкласса называется ковариантным переопределением методов и доступен с Java 5, что позволяет избежать приведения типа на стороне клиента:

Course javaBeginners = new Course("Java", 100, 10);
Course clone = javaBeginners.clone();


Ранее, например, с классом Date, нужно было явно приводить результат метода clone():

Date d = new Date();
Date clone = (Date) d.clone();


Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
➡️ Используйте интерфейсы, когда это возможно

Раньше я использовал конкретные классы, например ArrayList, Vector, и HashMap для возвращаемых значений и аргументов методов.

ℹ️ Это ограничивает гибкость. Вы не можете передать другой список, даже если он лучше, и при смене реализации нужно менять все места в коде.

✔️ Лучше использовать интерфейсы. Например, для упорядоченного списка с дубликатами используйте java.util.List, для неупорядоченного набора без дубликатов — java.util.Set, а для контейнера — Collection. Это позволит легко менять реализации.

Можно ещё больше упростить с помощью обобщений и extends. Например, вы можете использовать List<? extends Number>, что позволит передавать List<Integer> или List<Short>.


Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍92
➡️ Использование Iterator для перебора List

В Java есть несколько способов перебрать список: цикл for с индексом, расширенный for и Iterator. Раньше я использовал цикл for с методом get(), как показано ниже:

for (int i = 0; i < list.size(); i++) {
String name = list.get(i);
}


🗣️ Этот метод работает для ArrayList, но если список — это LinkedList или другая реализация без поддержки случайного доступа, время выполнения увеличится до O(N^2), потому что get() для LinkedList имеет O(n) сложность.

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


✔️ Лучше использовать Iterator, который является стандартным способом перебора списка:

Iterator<String> itr = list.iterator();

while (itr.hasNext()) {
String name = itr.next();
}


Это безопаснее и предотвращает непредсказуемое поведение.

Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍161
⚙️ Использование Dependency Injection в коде

Ранее я писал код следующим образом:

public class Game {

private HighScoreService service = HighScoreService.getInstance();

public void showLeaderBoard() {
List listOfTopPlayers = service.getLeaderBoard();
System.out.println(listOfTopPlayers);
}

}


🗣️ Этот подход вызывает несколько проблем:

Класс Game жестко связан с классом HighScoreService, что усложняет тестирование Game в изоляции.

Даже при наличии класса HighScoreService сложно протестировать Game, если HighScoreService делает сетевые запросы или загружает данные с серверов. Мок-объекты здесь не подходят.

✔️ Решение: переписать класс Game с использованием Dependency Injection (DI).

Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Что будет выведено при выполнении кода?

Java Learning 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ответ:
Anonymous Quiz
11%
10
80%
15
2%
20
7%
5
👍6