Estruturas de dados em Java. Métodos úteis da classe auxiliar

Oi habr!

Sou Engenheiro de Software na EPAM. Por mais de 8 anos, trabalho com código legado escrito em Java (antecipando comentários, observo que o entendimento e a tolerância ao legado começaram muito antes do EPAM; em conclusão, você encontrará a resposta). Muitas vezes, no trabalho, encontrei as mesmas falhas repetidas. Isso me levou a escrever uma nota e quero começar com as estruturas de dados e as classes auxiliares Coleções e matrizes . Por alguma razão, alguns desenvolvedores negligenciam seu uso e, em vão

Um desenvolvedor Java geralmente precisa lidar com várias estruturas de dados. Pode ser matrizes, todos os tipos de coleções ou implementações do Map . Parece que tudo com eles é claro e compreensível, mas existem várias pequenas coisas fáceis de tropeçar.

Esta nota pode ser útil tanto para iniciantes que ainda não conhecem essas nuances quanto para desenvolvedores experientes que podem esquecer parte disso.

imagem
Foto de ammiel jr em Unsplash

CAT


Quero fazer uma reserva imediata de que esse material é relevante para o Java 8. É claro que algumas coisas já foram feitas melhor no Java 9+, mas a maioria dos projetos grandes costuma usar a versão do Java 8 (e às vezes o Java 6).

Qual é a melhor maneira de obter uma coleção baseada em matriz?


Sugiro começar com a formação de uma coleção baseada em uma matriz.

Na maioria das vezes, esse método ocorre:

Integer[] someArray = {9, 10, 11, 12}; List<Integer> list = Arrays.asList(someArray); 

Certamente funciona, mas está tudo bem com isso? E existem soluções alternativas?

Dois pontos negativos dessa abordagem vêm à mente de uma só vez:

  • Primeiro, o método Arrays.asList retorna uma lista . Mas e se precisarmos de outra implementação do Collection? Arrays.asList não permitirá isso, mas uma alternativa será considerada um pouco mais adiante.
  • Em segundo lugar, a lista obtida chamando Arrays.asList não suporta redimensionamento. Eu acho que muitos criaram uma exceção decorrente do trabalho com essa lista.

Na interface Coleções , você pode encontrar uma alternativa ao método Arrays.asList - o método Collections.addAll . Veja como usá-lo:

 //      (List, Set, ...) Collection<Integer> collection = ...; Integer[] someArray = {9, 10, 8, 7}; Collections.addAll(collection, someArray); 

Ou simplesmente:

 Collections.addAll(collection, 11, 12, 13, 14); 

O método Collections.addAll aceita um objeto Collection e uma matriz na entrada. Em vez de uma matriz, você também pode especificar elementos separados por vírgulas.

Quais são os benefícios do Collections.addAll over Arrays.asList ?

  • Para começar, ao criar coleções com base na matriz Collections.addAll , ele funciona muito mais rápido que o método addAll da coleção com a entrada Arrays.asList . Isso pode ser encontrado no JavaDoc deste método:
    O comportamento desse método de conveniência é idêntico ao de c.addAll (Arrays.asList (elementos)), mas é provável que esse método seja significativamente mais rápido na maioria dos casos.
  • Além disso, Collections.addAll funciona não apenas com List , mas com qualquer outra coleção.
  • E ao usar esse método, não há problema em redimensionar.

Qual é a maneira mais fácil de imprimir uma matriz, matriz multidimensional ou coleção?


Vamos passar à questão de obter uma representação impressa de uma matriz e coleções.

Se apenas criarmos System.out.println (someArray) , obteremos algo assim:
[Ljava.lang.Integer; @ 6d06d69c.
Um resultado semelhante é esperado ao usar o método toString () em uma matriz.
Para gerar a matriz, o método Arrays.toString (...) vem ao resgate .

 Integer[] someArray = new Integer[]{1, 2, 3}; System.out.println(Arrays.toString(someArray)); 

A saída para esta linha será:

 [1, 2, 3] 

Se estamos falando de uma matriz multidimensional, você pode usar o método: Arrays.deeptoString .

 int[][] a = { {1, 2, 3}, {4, 5, 6} }; System.out.println(Arrays.deepToString(a)); 

A saída desse snippet será:

  [[1, 2, 3], [4, 5, 6]] 


Portanto, não é necessário classificar a matriz através de qualquer loop manualmente para exibir seus elementos; basta usar esse método.

Quanto às coleções ou implementações do Map , não há problemas. Todas as estruturas de dados, exceto a matriz, são normalmente produzidas.

Suponha que exista um exemplo:

 Collection<Integer> someCollection = new HashSet<>(); someCollection.add(1); someCollection.add(2); System.out.println(someCollection); Map<Integer, String> someMap = new HashMap<>(); someMap.put(1, "Some 1"); someMap.put(2, "Some 2"); System.out.println(someMap); 

Observe na saída abaixo que o conjunto e o mapa foram exibidos em um formato fácil de ler:

 [1, 2] 
 {1 = Alguns 1, 2 = Alguns 2} 


Quão fácil é comparar matrizes entre si?


Há situações em que você precisa comparar matrizes. Existe um método na classe Arrays que permite essa comparação. O método Arrays.equals compara o número de elementos e verifica a equivalência dos elementos correspondentes.

Digamos que temos uma classe Elements com um campo e certos iguais

 private class Element { final String name; private Element(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Element element = (Element) o; return Objects.equals(name, element.name); } @Override public int hashCode() { return Objects.hash(name); } } 

Defina três matrizes:

 Element[] firstElementArray = { new Element("a"), new Element("b"), new Element("c") }; Element[] secondElementArray = {new Element("c"), new Element("b"), new Element("a") }; Element[] thirdElementArray = { new Element("a"), new Element("b"), new Element("c") }; 

Observe que a primeira e a terceira matrizes têm elementos na mesma ordem.
Agora você pode executar a comparação usando o método Arrays.equals .

 System.out.println("first equals to second? " + Arrays.equals(firstElementArray, secondElementArray)); System.out.println("second equals to third? " + Arrays.equals(secondElementArray, thirdElementArray)); System.out.println("first equals to third? " + Arrays.equals(firstElementArray, thirdElementArray)); 

O resultado será o seguinte:

 primeiro é igual a segundo?  falsa 
 segundo é igual a terceiro?  falsa 
 primeiro é igual a terceiro?  verdade 


Como copiar eficientemente uma matriz?


Muitas vezes você pode ver no código copiando manualmente matrizes usando loops. No entanto, existe um método System.arraycopy que copiará muito mais rapidamente.

Sugiro uma olhada em um exemplo tão simples:

 Element[] elements = { new Element("a"), new Element("b"), new Element("c") }; Element[] copyOfElements = new Element[elements.length]; System.arraycopy(elements, 0, copyOfElements, 0, elements.length); System.out.println(Arrays.toString(copyOfElements)); 

Temos uma matriz de elementos. Criamos uma matriz vazia do mesmo comprimento e copiamos todos os elementos do primeiro ao segundo. Como resultado, obtemos a seguinte conclusão:

 [Elemento {name = 'a'}, Elemento {name = 'b'}, Elemento {name = 'c'}] 


Como classificar uma matriz ou coleção de maneiras diferentes?


As matrizes podem ser classificadas usando o método Arrays.sort (someArray) . Se você deseja classificar a matriz na ordem inversa, pode passar Collections.reverseOrder () como o segundo parâmetro para a entrada desse método.

Por exemplo, há uma matriz que classificamos direta e depois na ordem inversa:

 String[] someArray = new String[]{"b", "a", "c"}; Arrays.sort(someArray); System.out.println(Arrays.toString(someArray)); Arrays.sort(someArray, Collections.reverseOrder()); System.out.println(Arrays.toString(someArray)); 

A conclusão será a seguinte:

 [a, b, c] 
 [c, b, a] 


Além da classificação direta e reversa, às vezes acontece que você precisa classificar uma matriz de seqüências de caracteres, independentemente do caso. Isso é fácil, passando String.CASE_INSENSITIVE_ORDER como o segundo parâmetro para Arrays.sort .

Infelizmente, o Collections.sort só permite que as implementações da Lista sejam classificadas.

Qual algoritmo classifica Java?


A última coisa a mencionar quando se fala em classificação em Java é que, em Java, "classificação simples" é usada para os tipos mais simples e "mesclagem estável" é usada para objetos. Portanto, você não deve gastar recursos desenvolvendo sua própria implementação do método de classificação até que o criador de perfil mostre que é necessário.

E se tivermos uma matriz e o método aceitar Iterable?


Proponho agora passar a uma pergunta como passar uma matriz para um método que requer Iterable . Deixe-me lembrá-lo de que Iterable é uma interface que contém o método iterator () , que o Iterator deve retornar.

Se houver um método que aceite Iterable na entrada, a matriz não poderá ser transferida para lá exatamente assim. Embora você possa iterar sobre uma matriz em um loop for , ele não é iterável .

 String[] someArray = new String[]{"a", "b", "c"}; for (String currentString : someArray) { ... } 

Neste exemplo, está tudo bem. Mas se houver um método:

 private static void someIteration(Iterable<String> iterable) { ... } 

Essa linha não será compilada:

 someIteration(someArray); 

A única saída nessa situação é converter a matriz em uma coleção e já a alimentar com esse método.

Brevemente sobre alguns métodos úteis de Coleções


MétodoComentário
max (Collection) e max (Collection, Comparator)
min (Coleção) e min (Coleção, Comparador)
Observe que você pode se inscrever na entrada do Comparator
indexOfSubList (List, List)
Localiza o índice da primeira ocorrência de uma lista (segundo argumento) em outra (primeiro argumento)
lastIndexOfSubList (List, List)
Localiza o índice da última ocorrência de uma lista (segundo argumento) em outra (primeiro argumento)
reverso (lista)
Reordena itens na ordem inversa

O que vale a pena ler?


Essa é apenas uma pequena parte das ferramentas que podem facilitar a vida do desenvolvedor ao trabalhar com estruturas de dados. Muitos pontos interessantes no trabalho das próprias coleções e ferramentas convenientes para trabalhar com elas podem ser encontrados no livro de Bruce Eckel, “Java Philosophy” (4ª edição completa). No entanto, você deve ter cuidado, pois encontra situações que não podem mais ser reproduzidas no Java 7, Java 8 e superior. Embora o Java 6 seja descrito neste livro, seu material permanece amplamente relevante hoje.

Obviamente, a “filosofia Java” não deve ser limitada. A leitura de qualquer um desses livros não prejudicará nenhum desenvolvedor Java:

  • "Java. Programação eficaz ”, Joshua Bloch.
  • “Refatoração. Melhorando o design do código existente ”, Martin Fowler.
  • “Código limpo. Criação, análise e refatoração ”, Robert Martin.
  • Primavera 5 para Profissionais, Julian Kozmin e outros.
  • "Desenvolvimento Java Orientado a Testes", Viktor Farcic, Alex Garcia (ainda não foi lançado em russo).

Qual é o resultado?


Se você teve idéias interessantes que poderiam complementar o que foi escrito neste artigo, compartilhe-as nos comentários.

Eu também gostaria de desejar boa sorte e paciência para aqueles que trabalham com o código antigo legado. A maioria dos grandes projetos é legada. E é difícil superestimar o significado deles para o cliente. E o sentimento de vitória de eliminar o bug, que levou mais de uma semana para encontrar os motivos, não é inferior aos sentimentos no final da implementação de um novo recurso.

Obrigado pela atenção. Eu ficaria feliz se algum dos apresentados fosse útil.
Todo o sucesso!

Source: https://habr.com/ru/post/pt476098/


All Articles