Перейти к основному содержимому

Гуава 19: что нового?

· 8 мин. чтения

1. Обзор

Google Guava предоставляет библиотекам утилиты, облегчающие разработку Java. В этом уроке мы рассмотрим новые функции, представленные в выпуске Guava 19 .

2. Изменения пакета common.base

2.1. Добавлены статические методы CharMatcher

CharMatcher , как следует из названия, используется для проверки соответствия строки набору требований.

String inputString = "someString789";
boolean result = CharMatcher.javaLetterOrDigit().matchesAllOf(inputString);

В приведенном выше примере результат будет истинным .

CharMatcher также можно использовать, когда вам нужно преобразовать строки.

String number = "8 123 456 123";
String result = CharMatcher.whitespace().collapseFrom(number, '-');

В приведенном выше примере результатом будет «8-123-456-123».

С помощью CharMatcher вы можете подсчитать количество вхождений символа в заданную строку:

String number = "8 123 456 123";
int result = CharMatcher.digit().countIn(number);

В приведенном выше примере результатом будет 10.

В предыдущих версиях Guava были константы сопоставления, такие как CharMatcher.WHITESPACE и CharMatcher.JAVA_LETTER_OR_DIGIT .

В Guava 19 они были заменены эквивалентными методами ( CharMatcher.whitespace() и CharMatcher.javaLetterOrDigit() соответственно). Это было изменено, чтобы уменьшить количество классов, создаваемых при использовании CharMatcher .

Использование статических фабричных методов позволяет создавать классы только по мере необходимости. В будущих выпусках константы сопоставления будут объявлены устаревшими и удалены.

2.2. Метод lazyStackTrace в Throwables

Этот метод возвращает список элементов трассировки стека (строк) предоставленного Throwable . Это может быть быстрее, чем повторение всей трассировки стека ( Throwable.getStackTrace() ), если требуется только часть, но может быть медленнее, если вы будете перебирать всю трассировку стека.

IllegalArgumentException e = new IllegalArgumentException("Some argument is incorrect");
List<StackTraceElement> stackTraceElements = Throwables.lazyStackTrace(e);

3. Изменения пакета common.collect

3.1. Добавлен FluentIterable.toMultiset()

В предыдущей статье ForEach Что нового в Guava 18 мы рассмотрели FluentIterable . Метод toMultiset() используется, когда вам нужно преобразовать FluentIterable в ImmutableMultiSet .

User[] usersArray = {new User(1L, "John", 45), new User(2L, "Max", 15)};
ImmutableMultiset<User> users = FluentIterable.of(usersArray).toMultiset();

Multiset — это коллекция, такая как Set , которая поддерживает независимое от порядка равенство. Основное различие между набором и мультинабором заключается в том, что мультинабор может содержать повторяющиеся элементы. Multiset хранит одинаковые элементы как вхождения одного и того же элемента, поэтому вы можете вызвать Multiset.count(java.lang.Object) , чтобы получить общее количество вхождений данного объекта.

Давайте рассмотрим несколько примеров:

List<String> userNames = Arrays.asList("David", "ForEach", "Alex", "Alex", "David", "David", "David");

Multiset<String> userNamesMultiset = HashMultiset.create(userNames);

assertEquals(7, userNamesMultiset.size());
assertEquals(4, userNamesMultiset.count("David"));
assertEquals(2, userNamesMultiset.count("Alex"));
assertEquals(1, userNamesMultiset.count("ForEach"));
assertThat(userNamesMultiset.elementSet(), anyOf(containsInAnyOrder("Alex", "David", "ForEach")));

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

**3.2. Добавлен RangeSet.asDescendingSetOfRanges() и asDescendingMapOfRanges().

**

RangeSet используется для работы с непустыми диапазонами (интервалами). Мы можем описать RangeSet как набор несвязанных непустых диапазонов. Когда вы добавляете новый непустой диапазон в RangeSet , все связанные диапазоны будут объединены, а пустые диапазоны будут проигнорированы:

Давайте рассмотрим некоторые методы, которые мы можем использовать для создания новых диапазонов: Range.closed() , Range.openClosed() , Range.closedOpen() , Range.open() .

Разница между ними в том, что открытые диапазоны не включают свои конечные точки. Они имеют разное обозначение в математике. Открытые интервалы обозначаются «(» или «)», а закрытые диапазоны обозначаются «[» или «]».

Например, (0,5) означает «любое значение больше 0 и меньше 5», а (0,5] означает «любое значение больше 0 и меньше или равно 5»:

RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10));

Здесь мы добавили диапазон [1, 10] в наш RangeSet . И теперь мы хотим расширить его, добавив новый диапазон:

rangeSet.add(Range.closed(5, 15));

Вы можете видеть, что эти два диапазона соединены в 5, поэтому RangeSet объединит их в новый единый диапазон [1, 15]:

rangeSet.add(Range.closedOpen(10, 17));

Эти диапазоны соединены в 10, поэтому они будут объединены, в результате чего получится закрытый-открытый диапазон, [1, 17). Вы можете проверить, входит ли значение в диапазон или нет, используя метод contains :

rangeSet.contains(15);

Это вернет true , потому что диапазон [1,17) содержит 15. Давайте попробуем другое значение:

rangeSet.contains(17);

Это вернет false , потому что диапазон [1,17) не содержит своей верхней конечной точки, 17. Вы также можете проверить, охватывает ли диапазон какой-либо другой диапазон, используя метод encloses :

rangeSet.encloses(Range.closed(2, 3));

Это вернет true , потому что диапазон [2,3] полностью попадает в наш диапазон [1,17).

Есть еще несколько методов, которые могут помочь вам работать с интервалами, например Range.greaterThan() , Range.lessThan() , Range.atLeast() , Range.atMost() . Первые два добавят открытые интервалы, последние два добавят закрытые интервалы. Например:

rangeSet.add(Range.greaterThan(22));

Это добавит новый интервал (22, +∞) в ваш RangeSet , потому что он не связан с другими интервалами.

С помощью новых методов, таких как asDescendingSetOfRanges (для RangeSet ) и asDescendingMapOfRanges (для RangeSet ), вы можете преобразовать RangeSet в Set или Map .

3.3. Добавлены Lists.cartesianProduct(List…) и Lists.cartesianProduct(List<List>>)

Декартово произведение возвращает все возможные комбинации двух или более коллекций:

List<String> first = Lists.newArrayList("value1", "value2");
List<String> second = Lists.newArrayList("value3", "value4");

List<List<String>> cartesianProduct = Lists.cartesianProduct(first, second);

List<String> pair1 = Lists.newArrayList("value2", "value3");
List<String> pair2 = Lists.newArrayList("value2", "value4");
List<String> pair3 = Lists.newArrayList("value1", "value3");
List<String> pair4 = Lists.newArrayList("value1", "value4");

assertThat(cartesianProduct, anyOf(containsInAnyOrder(pair1, pair2, pair3, pair4)));

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

3.4. Добавлены Maps.newLinkedHashMapWithExpectedSize(int)

Начальный размер стандартного LinkedHashMap — 16 (вы можете убедиться в этом в исходниках LinkedHashMap ). Когда он достигает коэффициента загрузки HashMap (по умолчанию 0,75), HashMap повторно хеширует и удваивает свой размер. Но если вы знаете, что ваш HashMap будет обрабатывать много пар ключ-значение, вы можете указать начальный размер больше 16, что позволит вам избежать повторных перефразировок:

LinkedHashMap<Object, Object> someLinkedMap = Maps.newLinkedHashMapWithExpectedSize(512);

3.5. Повторно добавлены Multisets.removeOccurrences(Multiset, Multiset)

Этот метод используется для удаления указанных вхождений в Multiset :

Multiset<String> multisetToModify = HashMultiset.create();
Multiset<String> occurrencesToRemove = HashMultiset.create();

multisetToModify.add("John");
multisetToModify.add("Max");
multisetToModify.add("Alex");

occurrencesToRemove.add("Alex");
occurrencesToRemove.add("John");

Multisets.removeOccurrences(multisetToModify, occurrencesToRemove);

После этой операции в multisetToModify останется только «Max» .

Обратите внимание, что если multisetToModify содержит несколько экземпляров данного элемента, а instancesToRemove содержит только один экземпляр этого элемента, removeOccurrences удалит только один экземпляр.

4. Изменения пакета common.hash

4.1. Добавлено хеширование.sha384()

Метод Hashing.sha384() возвращает хеш-функцию, реализующую алгоритм SHA-384:

int inputData = 15;

HashFunction hashFunction = Hashing.sha384();
HashCode hashCode = hashFunction.hashInt(inputData);

SHA-384 имеет значение 15: «0904b6277381dcfbddd…2240a621b2b5e3cda8».

4.2. Добавлено Hashing.concatenating(HashFunction, HashFunction, HashFunction…) и Hashing.concatenating(Iterable<HashFunction>)

С помощью методов Hashing.concatenate вы объединяете результаты серии хеш-функций:

int inputData = 15;

HashFunction crc32Function = Hashing.crc32();
HashCode crc32HashCode = crc32Function.hashInt(inputData);

HashFunction hashFunction = Hashing.concatenating(Hashing.crc32(), Hashing.crc32());
HashCode concatenatedHashCode = hashFunction.hashInt(inputData);

Результирующий concatenatedHashCode будет «4acf27794acf2779», что совпадает с crc32HashCode («4acf2779»), объединенным с самим собой.

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

5. Изменения пакета common.reflect

5.1. Добавлен TypeToken.isSubtypeOf

TypeToken используется для манипулирования универсальными типами и запросов к ним даже во время выполнения, что позволяет избежать проблем из-за стирания типов .

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

В нашем примере вы можете видеть, что без метода TypeToken isAssignableFrom вернет true , даже если ArrayList<String> не может быть назначен из ArrayList<Integer> :

ArrayList<String> stringList = new ArrayList<>();
ArrayList<Integer> intList = new ArrayList<>();
boolean isAssignableFrom = stringList.getClass().isAssignableFrom(intList.getClass());

Чтобы решить эту проблему, мы можем проверить это с помощью TypeToken .

TypeToken<ArrayList<String>> listString = new TypeToken<ArrayList<String>>() { };
TypeToken<ArrayList<Integer>> integerString = new TypeToken<ArrayList<Integer>>() { };

boolean isSupertypeOf = listString.isSupertypeOf(integerString);

В этом примере isSupertypeOf вернет false.

В предыдущих версиях Guava для этих целей был метод isAssignableFrom , но начиная с Guava 19 он устарел в пользу isSupertypeOf . Кроме того, метод isSubtypeOf(TypeToken) можно использовать для определения того, является ли класс подтипом другого класса:

TypeToken<ArrayList<String>> stringList = new TypeToken<ArrayList<String>>() { };
TypeToken<List> list = new TypeToken<List>() { };

boolean isSubtypeOf = stringList.isSubtypeOf(list);

ArrayList является подтипом List , поэтому результат будет true , как и ожидалось.

6. Изменения пакета common.io

**6.1. Добавлен ByteSource.sizeIfKnown()

**

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

ByteSource charSource = Files.asByteSource(file);
Optional<Long> size = charSource.sizeIfKnown();

6.2. Добавлен CharSource.length()

В предыдущей версии Guava не было метода определения длины CharSource . Теперь вы можете использовать CharSource.length() для этой цели.

6.3. Добавлен CharSource.lengthIfKnown()

То же, что и для ByteSource, но с помощью CharSource.lengthIfKnown() вы можете определить длину вашего файла в символах:

CharSource charSource = Files.asCharSource(file, Charsets.UTF_8);
Optional<Long> length = charSource.lengthIfKnown();

7. Заключение

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

Примеры кода в этой статье доступны в репозитории GitHub .