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.
Foto de ammiel jr em UnsplashCAT
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:
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
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!