As linguagens de programação procuram dar suporte
à implementaçao do conceito de vetores e matrizes existentes
na matemática. Na linguagem Java a base deste suporte são
os arranjos. Um arranjo é constituído de uma seqüência
indexável de variáveis todas do mesmo tipo. É importante
enfatizar que, a menos de algum vício de linguagem, o termo "arranjo"
e "variável do tipo arranjo" são conceitos bem distintos.
Podemos indexar os elementos de um arranjo de forma similar à
notação da matemática. Os índices em Java sempre
começam de zero e o operador de indexação consiste
em abre e fecha colchetes - []. Assim a[0] denota o primeiro elemento
de um arranjodenominado a. A indexação de um arranjo
deve ser feita por uma expressão do tipo inteiro. Uma expressão
do tipo long não pode ser usada para indexar um arranjo.
Se tentarmos utilizar como índice um valor inválido, isto
é, um valor menor que zero ou maior que o valor do índice
do último elemento, será gerado um erro em tempo de execução:
ArrayIndexOutOfBoundsException
Na linguagem Java, além dos oito tipos primitivos - long, int, short, byte, char, float, double e boolean, existem as variáveis que podem conter endereços ou referências de memória. Em Java as variáveis do tipo arranjo são especializações de variáveis deste tipo, denominado tipo referência, ou seja as variáveis do tipo arranjo são também variáveis do tipo referência.
A linguagem Java permite que elementos de qualquer tipo possam se constituir
na forma de arranjo. Assim a expressão geral para o tipo "arranjo
de <tipo>" é "<tipo>[]" conforme exemplos abaixo:
int[] ai; /* variável do tipo arranjo
de inteiros */
long[] al; /* variável do tipo arranjo
de longos */
double[] ad; /* variável do tipo arranjo
de ponto flutuante duplo */
boolean[] ab; /* variável do tipo
arranjo de booleanos */
As variáveis do tipo arranjo podem ser iniciadas ou inicializadas
de forma semelhante às variáveis de tipos primitivos com
literais do tipo arranjo. Na verdade não existem literais do tipo
arranjo. As "constantes" ou "literais" do tipo arranjo são denominados
inicializadores de arranjo e não são considerados
literais ou constantes. Veja um trecho de programa com exemplo de declaração
de arranjo inicializado:
int[] numDiasMes={0, 31,28,31,30,31,30,31,31,
30, 31, 30, 31};
int totalDias=0;
for(int i=1; i<=12; i++) totalDias=totalDias+numDiasMes[i];
O trecho de programa acima declara uma variável do tipo arranjo denominada numDiasMes. Esta variável já é iniciada com uma referência para a região de memória onde existem 13 variáveis indexáveis do tipo inteiro. Cada variável inteira é iniciada com os 13 valores mostrados. Cada valor corresponde ao número de dias do mês de índice correspondente. A primeira variável de um arranjo em Java corresponde ao índice 0 (zero). Como o primeiro mês, Janeiro, é tradicionalmente denotado pelo número 1, o trecho acima opta por ignorar o primeiro elemento do arranjo - numDiasMes[0].
Os iniciadores de arranjo não podem ser utilizados nas expressões dos comandos de atribuição! O trecho de programa abaixo não é compilável:
int[] numDiasMes;
numDiasMes={0, 31,28,31,30,31,30,31,31, 30,
31, 30, 31}; /* erro! */
A iniciação ou inicialização de uma variável de tipo arranjo envolve duas operações: (1) alocação da seqüência de elementos ou variáveis do arranjo e (2) iniciação ou inicialização de cada um dos elementos do arranjo. A alocação da seqüência de elementos do arranjo se faz, em geral, com o uso do operador new. No exemplo abaixo vemos a alocação da seqüência de elementos do arranjo correspondente à variável numDiasMes, sem a inicialização dos elementos. O operador new retorna o endereço de memória ou referência para a memória onde foi feita a alocação do arranjo:
int[] numDiasMes;
numDiasMes=new int[13];
A linguagem Java não permite a especificação do número de elementos ou a especificação da dimensão do arranjo na declaração. Muitas linguagens de programação aceitam este estilo de delaração:
int[13] numDiasMes; /* erro!*/
A linguagem Java tem poucas operações que permitem a manipulação do arranjo como um todo. Conforme mencionado acima não existem "literais do tipo arranjo". O operador de alocação new permite formular expressões para comandos de atribuição envolvendo arranjos:
int[] numDiasMes;
numDiasMes= new int[]{0, 31,28,31,30,31,30,31,31,
30, 31, 30, 31};
O compilador não permite que o programador coloque a dimensão do arranjo e use o iniciador de arranjo:
int[] numDiasMes;
numDiasMes= new int[13]{0, 31,28,31,30,31,30,31,31,
30, 31, 30, 31}; /* erro! */
A figura abaixo mostra um modelo para o arranjo discutido acima:
O conteúdo da variável numDiasMes é uma referência
para um bloco de memória contíguo. A referência para
o bloco de memória é representada através de uma seta
com um disco na origem e uma seta. O bloco de memória ou arranjo
possui um campo ou variável denominada length. O campo
length
fornece quantas posições indexáveis tem o arranjo.
No caso da figura o campo length de numDiasMes indica que o arranjo
tem 13 elementos. O acesso a campos de blocos de memória acessados
por referências é feito através do operador de acesso
de campo: '. '. Assim, por exemplo, a expressão
numDiasMes.length
tem valor 13. O campo length não pode receber atribuições.
Em java é possível declarar variáveis que são
inicializadas e posteriormente não podem receber atribuições,
para isso usa-se o modificador final. Portanto o campo length do
arranjo acima poderia corresponder a uma declaração do tipo:
final int length=13;
A figura mostra os 13 valores com que os elementos do arranjo foram
inicializados e mostra também o índice de cada um deles,
começando com 0 e terminando com 12.
Uma variável do tipo arranjo, é uma variável do
tipo referência. Uma referência é simplesmente um endereço
de memória. O sistema de execução e o compilador Java
trabalham em conjunto para que, sempre que uma variável do tipo
arranjo não tenha uma referência válida o seu conteúdo
seja uma referência especial denominada null. O valor null
pode ser usado nos programas e consiste numa referência especial
cujo significado é "nenhuma referência". No trecho de programa
abaixo o comando if testa se a variável x faz referência para
um arranjo:
int[] x;
...
if(x==null) System.out.println("x nao contem
referencia para um arranjo");
Podemos usar o literal null nas inicializações
e atribuições de variáveis do tipo arranjo:
int[] x=null;
int[] y;
y=null;
É comum encontrarmos na literatura a representação
gráfica do null através do símbolo de aterramento.
Considere o seguinte comando de atribuição:
numDiasMes=null;
A representação gráfica para o conteúdo
da variável numDiasMes ficaria:
O operador new é que estrutura e aloca todo o bloco de memória para o arranjo fazendo todas as inicializações necessárias e retornando o endereço do bloco de memória. No caso de iniciações ou inicializações é utilizado, pelo compilador, um operador new de forma implícita. Veremos mais a frente na disciplina que um arranjo é um tipo especial de objeto e que a alocação de um objeto e o retorno de seu endereço é feito pelo operador new. Podemos fazer quantas atribuições quisermos a uma variável do tipo arranjo. Quando um bloco de memória previamente referido por uma variável do tipo arranjo fica sem nenhuma referência um mecanismo da Máquina Virtual Java denominado Coletor de Lixo irá cuidar de reciclar a memória e o bloco de memória poderá ser reutilizado. O ambiente de execução da linguagem Java não exige que o programador faça a gerência dos blocos de memória.
int[] ai1={1,2,3};
int[] ai2={4,5,6};
ai2=ai1;
No trecho de programa acima o bloco de memória correspondente ao arranjo {4,5,6} deixa de ser referido pela variável ai2 em função do comando de atribuição ai2=ai1. o arranjo {1,2,3} passa a ter duas referências. A partir deste momento o Coletor de Lixo da linguagem Java poderá atuar e reciclar o bloco de memória correspondente ao arranjo {4,5,6}.
Se tentarmos imprimir (System.out.print ou System.out.println) uma referência para um arranjo será impresso uma cadeia de caracteres que não se relaciona com o conteúdo do arranjo, mas que identifica a instância de arranjo. A única exceção é o tipo "arranjo de caracteres". System.out.print(A) e System.out.println(A) imprimem a sequência de caracteres correspondente a A quando A é um arranjo de caracteres. Exemplo
char[] c={'A', 'B', 'C'};
System.out.println(c);
imprime
ABC
A representação de uma matriz na linguagem Java pode ser
feita utilizando um arranjo de arranjos. Veja abaixo o exemplo de representação
de uma matriz identidade 3x3 utilizando um arranjo de arranjos de inteiros
e utilizando também o mecanismo de inicialização:
int[][] matIdent={ {1,0,0}, {0,1,0}, {0,0,1}};
Dizemos que matIdent é do tipo "arranjo de arranjos de inteiros".
Outra forma de declarar e inicializar o arranjo acima:
int[][] matIdent=new int[3][];
matIdent[0]=new int[]{1,0,0};
matIdent[1]=new int[]{0,1,0};
matIdent[2]=new int[]{0,0,1};
Ou ainda:
int[][] matIdent;
matIdent=new int[3][];
matIdent[0]=new int[]{1,0,0};
matIdent[1]=new int[]{0,1,0};
matIdent[2]=new int[]{0,0,1};
Uma outra opção é utilizar
referências auxiliares (linha1, linha2 e linha3):
int[][] matIdent=new int[3][];
int[] linha1={1,0,0};
int[] linha2={0,1,0};
int[] linha3={0,0,1};
matIdent[0]=linha1;
matIdent[1]=linha2;
matIdent[2]=linha3;
Nos trechos acima a variável matIdent é do tipo "arranjo de arranjos de inteiros", a variável denotada por matIdent[0] é do tipo "arranjo de inteiros" e a variável denotada por matIdent[0][0] é do tipo inteiro. Mais a frente na disciplina veremos que arranjos têm relação com classes e objetos: um "arranjo de inteiros" corresponde a uma classe e int[]{1,0,0} corresponde a uma instância ou objeto desta classe.
Exercícios:
Os arranjos nos permitem expressar alguns algoritmos para ordenação
dos valores.