Goiaba 19: O que há de novo?
1. Visão geral
Google Guava fornece bibliotecas com utilitários que facilitam o desenvolvimento Java. Neste tutorial, daremos uma olhada nas novas funcionalidades introduzidas noGuava 19 release.
2. common.base Mudanças de pacote
2.1. Métodos estáticos deCharMatcher adicionados
CharMatcher, como o próprio nome indica, é usado para verificar se uma string corresponde a um conjunto de requisitos.
String inputString = "someString789";
boolean result = CharMatcher.javaLetterOrDigit().matchesAllOf(inputString);
No exemplo acima,result serátrue.
CharMatcher também pode ser usado quando você precisa transformar strings.
String number = "8 123 456 123";
String result = CharMatcher.whitespace().collapseFrom(number, '-');
No exemplo acima,result será “8-123-456-123”.
Com a ajuda deCharMatcher, você pode contar o número de ocorrências de um caractere em uma determinada string:
String number = "8 123 456 123";
int result = CharMatcher.digit().countIn(number);
No exemplo acima,result será 10.
As versões anteriores do Guava têm constantes correspondentes, comoCharMatcher.WHITESPACEeCharMatcher.JAVA_LETTER_OR_DIGIT.
No Guava 19, eles foram substituídos por métodos equivalentes (CharMatcher.whitespace() eCharMatcher.javaLetterOrDigit(), respectivamente). Isso foi alterado para reduzir o número de classes criadas quandoCharMatcher é usado.
O uso de métodos estáticos de fábrica permite que as classes sejam criadas apenas conforme necessário. Em versões futuras, as constantes correspondentes serão descontinuadas e removidas.
Método2.2. lazyStackTrace emThrowables
Este método retornaList de elementos de rastreamento de pilha (linhas) de umThrowable fornecido. Pode ser mais rápido do que iterar pelo stacktrace completo (Throwable.getStackTrace()) se apenas uma parte for necessária, mas pode ser mais lento se você iterar por todo o stacktrace.
IllegalArgumentException e = new IllegalArgumentException("Some argument is incorrect");
List stackTraceElements = Throwables.lazyStackTrace(e);
3. common.collect Mudanças de pacote
3.1. AdicionadoFluentIterable.toMultiset()
Em um artigo de exemplo anterior,Whats new in Guava 18, vimosFluentIterable. O métodotoMultiset() é usado quando você precisa converter umFluentIterable em umImmutableMultiSet.
User[] usersArray = {new User(1L, "John", 45), new User(2L, "Max", 15)};
ImmutableMultiset users = FluentIterable.of(usersArray).toMultiset();
UmMultiset é uma coleção, comoSet, que suporta igualdade independente de ordem. A principal diferença entre aSet e aMultiset é queMultiset pode conter elementos duplicados. Multiset armazena elementos iguais como ocorrências do mesmo elemento único, então você pode chamarMultiset.count(java.lang.Object) para obter a contagem total de ocorrências de um determinado objeto.
Vamos dar uma olhada em alguns exemplos:
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")));
Você pode determinar facilmente a contagem de elementos duplicados, o que é muito mais limpo do que com as coleções Java padrão.
3.2. AdicionadoRangeSet.asDescendingSetOfRanges() easDescendingMapOfRanges()
RangeSet é usado para operar com faixas não vazias (intervalos). Podemos descrever aRangeSet como um conjunto de intervalos desconectados e não vazios. Quando você adiciona um novo intervalo não vazio a umRangeSet, todos os intervalos conectados serão mesclados e os intervalos vazios serão ignorados:
Vamos dar uma olhada em alguns métodos que podemos usar para construir novos intervalos:Range.closed(),Range.openClosed(),Range.closedOpen(),Range.open().
A diferença entre eles é que os intervalos abertos não incluem seus terminais. Eles têm designação diferente em matemática. Intervalos abertos são indicados com "(" ou ")", enquanto intervalos fechados são indicados com "[" ou "]".
Por exemplo (0,5) significa "qualquer valor maior que 0 e menor que 5", enquanto (0,5) significa "qualquer valor maior que 0 e menor que ou igual a 5":
RangeSet rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10));
Aqui adicionamos o intervalo [1, 10] ao nossoRangeSet. E agora queremos estendê-lo adicionando um novo intervalo:
rangeSet.add(Range.closed(5, 15));
Você pode ver que esses dois intervalos estão conectados em 5, entãoRangeSet os mesclará em um novo intervalo único, [1, 15]:
rangeSet.add(Range.closedOpen(10, 17));
Esses intervalos são conectados em 10 e, portanto, serão mesclados, resultando em um intervalo aberto-fechado, [1, 17). Você pode verificar se um valor está incluído no intervalo ou não usando o métodocontains:
rangeSet.contains(15);
Isso retornarátrue, porque o intervalo [1,17) contém 15. Vamos tentar outro valor:
rangeSet.contains(17);
Isso retornaráfalse, porque o intervalo [1,17) não contém seu ponto de extremidade superior, 17. Você também pode verificar se o intervalo inclui qualquer outro intervalo usando o métodoencloses:
rangeSet.encloses(Range.closed(2, 3));
Isso retornarátrue porque o intervalo [2,3] está completamente dentro do nosso intervalo, [1,17).
Existem mais alguns métodos que podem ajudá-lo a operar com intervalos, comoRange.greaterThan(),Range.lessThan(),Range.atLeast(),Range.atMost(). Os dois primeiros adicionam intervalos abertos, os dois últimos adicionam intervalos fechados. Por exemplo:
rangeSet.add(Range.greaterThan(22));
Isso adicionará um novo intervalo (22, + ∞) ao seuRangeSet, porque ele não tem conexões com outros intervalos.
Com a ajuda de novos métodos, comoasDescendingSetOfRanges (paraRangeSet) easDescendingMapOfRanges (paraRangeSet), você pode converter aRangeSet emSet ouMap.
3.3. AdicionadoLists.cartesianProduct(List…) eLists.cartesianProduct(List<List>>)
Um produto cartesiano retorna todas as combinações possíveis de duas ou mais coleções:
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)));
Como você pode ver neste exemplo, a lista resultante conterá todas as combinações possíveis de listas fornecidas.
3.4. AdicionadoMaps.newLinkedHashMapWithExpectedSize(int)
O tamanho inicial de umLinkedHashMap padrão é 16 (você pode verificar isso na fonte deLinkedHashMap). Quando atinge o fator de carga deHashMap (por padrão, 0,75),HashMap irá refazer e dobrar seu tamanho. Mas se você sabe que seuHashMap vai lidar com muitos pares de valores-chave, você pode especificar um tamanho inicial maior que 16, o que permite evitar repetidas reformulações:
LinkedHashMap
3.5. Multisets.removeOccurrences(Multiset, Multiset) adicionado novamente
Este método é usado para remover ocorrências especificadas emMultiset:
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);
Após esta operação, apenas “Max” permanecerá emmultisetToModify.
Observe que, semultisetToModify continha várias instâncias de um determinado elemento, enquantooccurrencesToRemove contém apenas uma instância desse elemento,removeOccurrences removerá apenas uma instância.
4. common.hash Package Changes
4.1. Adicionado Hashing.sha384 ()
O métodoHashing.sha384() retorna uma função hash que implementa o algoritmo SHA-384:
int inputData = 15;
HashFunction hashFunction = Hashing.sha384();
HashCode hashCode = hashFunction.hashInt(inputData);
O SHA-384 possui para 15 é "0904b6277381dcfbddd… 2240a621b2b5e3cda8".
4.2. AdicionadoHashing.concatenating(HashFunction, HashFunction, HashFunction…) eHashing.concatenating(Iterable<HashFunction>)
Com a ajuda dos métodosHashing.concatenating, você concatena os resultados de uma série de funções hash:
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);
OconcatenatedHashCode resultante será “4acf27794acf2779”, que é o mesmo quecrc32HashCode (“4acf2779”) concatenado consigo mesmo.
Em nosso exemplo, um único algoritmo de hash foi usado para maior clareza. Isso não é particularmente útil, no entanto. Combinar duas funções de hash é útil quando você precisa torná-lo mais forte, pois só pode ser quebrado se dois de seus hashes estiverem quebrados. Na maioria dos casos, use duas funções de hash diferentes.
5. common.reflect Mudanças de pacote
5.1. AdicionadoTypeToken.isSubtypeOf
TypeToken é usado para manipular e consultar tipos genéricos mesmo em tempo de execução, evitandoproblems due to type erasure.
Java não retém informações de tipo genérico para objetos em tempo de execução, então é impossível saber se um determinado objeto tem um tipo genérico ou não. Mas com a ajuda da reflexão, você pode detectar tipos genéricos de métodos ou classes. TypeToken usa esta solução alternativa para permitir que você trabalhe e consulte tipos genéricos sem código extra.
Em nosso exemplo, você pode ver que, sem o métodoTypeTokenisAssignableFrom, retornarátrue, emboraArrayList<String> não seja atribuível deArrayList<Integer>:
ArrayList stringList = new ArrayList<>();
ArrayList intList = new ArrayList<>();
boolean isAssignableFrom = stringList.getClass().isAssignableFrom(intList.getClass());
Para resolver este problema, podemos verificar isso com a ajuda deTypeToken.
TypeToken> listString = new TypeToken>() { };
TypeToken> integerString = new TypeToken>() { };
boolean isSupertypeOf = listString.isSupertypeOf(integerString);
Neste exemplo,isSupertypeOf retornará falso.
Em versões anteriores do Guava, havia o métodoisAssignableFrom para este propósito, mas a partir do Guava 19, ele foi substituído porisSupertypeOf. Além disso, o métodoisSubtypeOf(TypeToken) pode ser usado para determinar se uma classe é um subtipo de outra classe:
TypeToken> stringList = new TypeToken>() { };
TypeToken list = new TypeToken() { };
boolean isSubtypeOf = stringList.isSubtypeOf(list);
ArrayList é um subtipo deList, então o resultado serátrue, como esperado.
6. common.io Mudanças de pacote
6.1. AdicionadoByteSource.sizeIfKnown()
Este método retorna o tamanho da fonte em bytes, se puder ser determinado, sem abrir o fluxo de dados:
ByteSource charSource = Files.asByteSource(file);
Optional size = charSource.sizeIfKnown();
6.2. AdicionadoCharSource.length()
Na versão anterior do Guava não havia nenhum método para determinar o comprimento de aCharSource. Agora você pode usarCharSource.length() para essa finalidade.
6.3. AdicionadoCharSource.lengthIfKnown()
O mesmo que paraByteSource,, mas comCharSource.lengthIfKnown() você pode determinar o comprimento do seu arquivo em caracteres:
CharSource charSource = Files.asCharSource(file, Charsets.UTF_8);
Optional length = charSource.lengthIfKnown();
7. Conclusão
O Guava 19 introduziu muitas adições e melhorias úteis em sua crescente biblioteca. Vale a pena considerar o uso em seu próximo projeto.
Os exemplos de código neste artigo estão disponíveis emGitHub repository.