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

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

1. обзор

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

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

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

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

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

В приведенном выше примереresult будетtrue.

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

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

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

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

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

В приведенном выше примереresult будет 10.

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

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

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

2.2. lazyStackTrace Метод вThrowables

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

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

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

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

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

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

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

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

List userNames = Arrays.asList("David", "Eugen", "Alex", "Alex", "David", "David", "David");

Multiset userNamesMultiset = HashMultiset.create(userNames);

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

Вы можете легко определить количество повторяющихся элементов, что намного чище, чем в стандартных коллекциях 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 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 first = Lists.newArrayList("value1", "value2");
List second = Lists.newArrayList("value3", "value4");

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

List pair1 = Lists.newArrayList("value2", "value3");
List pair2 = Lists.newArrayList("value2", "value4");
List pair3 = Lists.newArrayList("value1", "value3");
List 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 someLinkedMap = Maps.newLinkedHashMapWithExpectedSize(512);

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

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

Multiset multisetToModify = HashMultiset.create();
Multiset occurrencesToRemove = HashMultiset.create();

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

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

Multisets.removeOccurrences(multisetToModify, occurrencesToRemove);

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

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

4. common.hash Package Changes

4.1. Добавлен Hashing.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.concatenating вы объединяете результаты ряда хэш-функций:

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 используется для управления универсальными типами и запросов к ним даже во время выполнения, избегаяproblems due to type erasure.

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

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

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

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

TypeToken> listString = new TypeToken>() { };
TypeToken> integerString = new TypeToken>() { };

boolean isSupertypeOf = listString.isSupertypeOf(integerString);

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

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

TypeToken> stringList = new TypeToken>() { };
TypeToken list = new TypeToken() { };

boolean isSubtypeOf = stringList.isSubtypeOf(list);

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

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

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

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

ByteSource charSource = Files.asByteSource(file);
Optional 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 length = charSource.lengthIfKnown();

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

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

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