DEV Community

Cover image for array[]: uma classe especial gerenciada internamente pela própria JVM
Ana Carolina Cortez
Ana Carolina Cortez

Posted on • Edited on

array[]: uma classe especial gerenciada internamente pela própria JVM

A primeira forma como todos nós somos introduzidos ao estudo de estruturas de dados em Java (e em outras linguagens) é por meio de arrays. Em Java, array é uma classe, um objeto, filha de Object (como todas as classes são). Porém, ela é uma classe com tratamento especial na linguagem.

Ao contrário de classes e objetos comuns, a sintaxe de arrays e seu tratamento é gerenciado diretamente pelo compilador e pela JVM (Java Virtual Machine). Isso inclui a forma como os arrays são alocados, manipulados e acessados. Essa classe, inclusive, não é encontrada diretamente no código-fonte.

O Java trata automaticamente os arrays como instâncias dessa classe especial.

Se você rodar o código abaixo vai perceber os seguintes outputs:

public class Main
{
    public static void main(String[] args) {
        int[] intArray = new int[5];
        System.out.println(intArray.getClass().getName());
        System.out.println(intArray.getClass().getSuperclass());
    }
}

Enter fullscreen mode Exit fullscreen mode

[I
class java.lang.Object

Esse nome de classe "[I" é apenas um nome simbólico gerado automaticamente, que a JVM usa para representar um array de inteiros (int[]). Cada tipo tem seu próprio nome simbólico:

Image description

Os colchetes "[" indicam as dimensões. Um array unidimensional é repreentado por [, enquanto o bidimensional por [[, o tridimensional por [[[... e assim por diante.

Declaração

Para declarar e inicializar um array, é importante explicitar o tipo de dado e o tamanho do objeto:

int[] intArray = new int[5]; //array de tamanho 5
char[] charArray = new char[]{'a', 'b', 'c'}; //o compilador entende que é um array de tamanho 3
double[] doubleArray = {1.2, 1.3, 1.4, 1.5}; //o compilador entende que é um array de tamanho 4
Enter fullscreen mode Exit fullscreen mode

O Java permite colocar os colchetes no nome da variável, em vez do tipo, por exemplo: int intArray[] = new int[5]. Porém, isso não é recomendado, pois foge da convenção.

Os dados armazenados dentro de arrays podem ser alocados na memória de forma "contígua", ou seja, sequencialmente, pela JVM. Para acessar os dados, utilizam-se índices e o primeiro índice de um array é sempre 0 em Java.

Por exemplo, para acessar a letra 'a' do charArray acima, é preciso buscá-lo por charArray[0]. A letra 'b', por sua vez, está em charArray[1] e, a letra 'c', em charArray[2]. Se você tentar acessar um índice que não existe no array, você receberá um erro de "IndexOutOfBounce". Por exemplo, no caso de eu tentar acessar o valor em charArray[3].

O tamanho de um array, uma vez definido na declaração, não poderá jamais ser alterado. Se eu declarei que charArray teria 3 elementos, ele não vai comportar mais que isso. Menos sim... mais, não.

E aqui, um adendo. Os valores não preenchidos de um array assumirão o mesmo valor padrão do tipo do array. Por exemplo, em um array de inteiros, os valores vazios serão preenchidos com 0. Em um array de boolean, com false. Em um array de string, com null.

Assim como o tamanho, o tipo de um array não pode ser alterado. Mas é possível copiar arrays com tipos diferentes, se estes forem subtipos de um mesmo tipo. Confuso, né? Um exemplo ajuda: Integer e Double são subtipos de Number. Então...

Integer[] integerArray = {1, 2, 3};
Number[] numberArray = intgerArray;
Enter fullscreen mode Exit fullscreen mode

Esse código é aceito pelo compilador. Porém, é preciso cautela. Se fizermos isso aqui, geraremos um erro de compilação:

numberArray[0] = 1.2;
Enter fullscreen mode Exit fullscreen mode

Isso porque integerArray e numberArray apontam para o mesmo espaço de memória. Ainda que numberArray comporte um double como primeiro elemento, o integerArray não comporta e, portanto, o numberArray não tem "permissão" de alterar o valor de índice 0 por um tipo double.

Memória

Um array pode armazenar sempre dados de um mesmo tipo, aceitando primitivos e objetos. Se o array é de inteiros, ele não vai aceitar char, double, float, String... somente valores inteiros.

O array em si, é uma classe (ainda que especial) e, portanto, é guardado na Heap. Desta forma, o array armazena um endereço de memória que, por sua vez, contém os valores inputados no array.

Um array de primitivos aloca blocos de memória contíguos para armazenar os valores diretamente, enquanto o array de objetos armazena referências (ponteiros) para objetos, localizados em outros lugares da memória.

Isso significa que tanto os arrays de tipos primitivos (como int[]) quanto os arrays de objetos (como String[]) são armazenados na heap. No caso de arrays de tipos primitivos, os valores dos elementos do array também são armazenados diretamente na heap, contiguamente ao próprio array. Já no caso do array de objetos, os objetos apontados por essas referências podem estar alocados em diferentes locais da heap.

Métodos

Todas as classes de array tem os métodos length e cloning. O primeiro retorna o tamanho do array e, o segundo, faz uma cópia do array para um outro array (no caso, do ponteiro para o mesmo endereço de memória).

Porém, como filho da classe Object (assim como todas as classes em Java), o array possui também os métodos da superclasse: toString, equals e hashCode.

Utilizar arrays por baixo dos panos, contudo, é o que mais acontece na vida real. Ainda que os arrays sejam performáticos, é bem mais complicado iterar os seus elementos e há classes que fazem abstrações em cima de arrays que tornam a vida do programador muito mais simples.

Esse é o caso da classe Arrays (com letra maiúscula). Ela basicamente empacota arrays em uma classe padrão do Java e apresenta uma série de métodos bem simples de implementar para trabalhar com os dados. A classe Arrays tem mais uma vantagem poderosa: ela trabalha com alocação dinâmica, então é mais fácil de lidar com coleções - afinal, raramente a gente sabe o tamanho exato do array que podemos precisar. A classe Arrays consegue expandir o tamanho da coleção sob demanda, sem que o programador tenha que ficar criando novos arrays (com letra minúscula) de tamanhos maiores e copiando os dados do array anterior, que ficou pequeno demais de repente.

Os arrays também são a base de classes como List, Stack e Queue (que fazem basicamente um wrap e incluem métodos muito bons para lidar com os dados).

Já parou para pensar que String é uma classe que abstrai um array de char?

Image description

Dimensões

Um array multidimensional é um array de objetos, já que um array é um objeto em si. Por exemplo:

int[][] arrayInt = new int[5][2];
Enter fullscreen mode Exit fullscreen mode

Neste caso, é correto dizer que o array de int recebe 5 objetos de tamanho 2. Em Java, um array bidimensional é na verdade um array de arrays unidimensionais. Ou seja, no exemplo, temos um array contendo 5 elementos, onde cada elemento é um array de 2 inteiros (int[]).

O arrayInt é um array de 5 referências (objetos). Cada uma dessas referências aponta para um array unidimensional de 2 inteiros (int[]).
Em termos mais simples, você tem 5 objetos (arrays unidimensionais) de 2 inteiros cada um.

Para iterar sobre arrays multidimensionais geralmente utiliza-se uma cadeia de loops.

int[][] arrayInt = new int[5][2]; // Array 5x2

int value = 1; // Valor inicial para preencher o array

for (int i = 0; i < arrayInt.length; i++) {           // Itera sobre as linhas (5 linhas)
    for (int j = 0; j < arrayInt[i].length; j++) {     // Itera sobre as colunas (2 colunas)
        arrayInt[i][j] = value++;                      // Atribui o valor e incrementa
    }
}

Enter fullscreen mode Exit fullscreen mode

Resultado:
[
[1, 2],
[3, 4],
[5, 6],
[7, 8],
[9, 10]
]

Arrays são objetos e eles pondem apontar para outros objetos. Mas, é preciso cautela nesse entendimento.

Object[] oa = new int[2][3];
Enter fullscreen mode Exit fullscreen mode

Isso compila porque o tipo int[2][3] é um array bidimensional de inteiros, ou seja, é um array de arrays de inteiros. Um array de qualquer tipo é um objeto em Java, e arrays multidimensionais são arrays de arrays. Portanto, int[2][3] é na verdade um array de int[], e você está atribuindo este array de int[] a uma variável do tipo Object[]. Isso é permitido, pois um array de qualquer tipo pode ser armazenado como Object[].

Mas...

Object[][] oaa = new int[2][3];
Enter fullscreen mode Exit fullscreen mode

Não compila. Em Java, arrays têm tipos fortes e a relação entre int[] e Object[] não se aplica aqui. Um array de int não é considerado um array de Object[], porque tipos primitivos (int, char, etc.) não podem ser diretamente atribuídos a variáveis do tipo Object.

O compilador espera que cada elemento de Object[][] seja do tipo Object[], mas int[] não pode ser convertido implicitamente para Object[], pois int é um tipo primitivo.

Em suma, o primeiro compila porque um array bidimensional de inteiros é tratado como um array de arrays (int[][]), que pode ser armazenado como Object[]. O segundo não compila porque um array de int não pode ser atribuído a um array de Object[].

Top comments (0)