Pensamento Computacional - Arrays Unidimensionais no Java

De MediaWiki do Campus São José
Revisão de 16h44min de 1 de dezembro de 2023 por Eraldo (discussão | contribs) (→‎EXEMPLO)
Ir para navegação Ir para pesquisar

Conceito de Array

Um array unidimensional ou vetor pode ser visto como uma variável (uma caixa) dividida em partes menores (CAIXAS menores) acessadas por um índice (posição). Podemos dizer que cada elemento do vetor pode ser acessado através da indexação do vetor. Os elementos do vetor possuem um tipo único.


PRG1-vetor armario.png


Uma boa analogia é comparar o vetor com uma tabela de tamanho fixo onde em cada linha pode ser armazenado um elemento.


PRG1-tabela vetor.png

Observe que com o mecanismo de criação de vetor será possível armazenar dados de uma forma organizada e de forma que se possa operar em diversos momentos do processamento.

Exemplo 1

Suponha que em uma determinada turma de 5 alunos queremos armazenar os valores das notas finais (valores inteiros de 0 a 10).

Neste caso o tamanho do array será 5. As indexações possíveis do array serão de 0 a 4 portanto. O tipo de cada elemento é inteiro, devido as notas inteiras.

Uma possível solução para isso seria:

import java.util.Scanner;

public class TabelaNotas {
    public static void main(String[] args) {
        int[]  TabelaNotas = new int[5];
        Scanner teclado = new Scanner(System.in);

        for (int i = 0; i < 5; i++) {
            System.out.println("Entre com a nota do aluno " + i);
            TabelaNotas[i] = teclado.nextInt();
        }
        System.out.println("Dados armazenados!!!");
        /* imprimindo os dados lidos... */
        for (int i = 0; i < 5; i++) {
            System.out.println("Nota da posição " + i + " é " + TabelaNotas[i]);
        }
        teclado.close();
    }
}

DISCUSSÃO:

Inicialmente a instrução:

int[]  TabelaNotas = new int[5];

Declara a referência para o vetor TabelaNotas e cria um vetor de tamanho 5 associado a esta referÊncia. De agora em diante, podemos trabalhar este vetor (lendo e escrevendo sobre cada elemento dele).

Observe que as 5 notas serão "lidas" para o vetor de inteiros TabelaNotas. Preste atenção a forma como é indexado o vetor:

            TabelaNotas[i] = teclado.nextInt();

Verifique o uso de colchetes com o índice do elemento que está sendo acessado. Na realidade, o índice pode ser qualquer expressão inteira que resulte em 0 a 4. Um ponto importante a ser observado é que este limite da indexação deve ser respeitado. Vamos a um exemplo que teremos problemas por acesso indevido:

import java.util.Scanner;

public class TabelaNotas {
    public static void main(String[] args) {
        int[]  TabelaNotas = new int[5];
        Scanner teclado = new Scanner(System.in);

        for (int i = 0; i < 5; i++) {
            System.out.println("Entre com a nota do aluno " + i);
            TabelaNotas[i+5] = teclado.nextInt();
        }
        System.out.println("Dados armazenados!!!");
        /* imprimindo os dados lidos... */
        for (int i = 0; i < 5; i++) {
            System.out.println("Nota da posição " + i + " é " + TabelaNotas[i]);
        }
        teclado.close();
    }
}

DISCUSSÃO : Ao tentar executar este código teremos um erro logo de início:

Entre com a nota do aluno 0
9
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
       at TabelaNotas.main(TabelaNotas.java:11)

A instrução:

           TabelaNotas[i+5] = teclado.nextInt();

logo no primeiro laço do loop, quando i estiver em 0, tentará escrever na posição 5 do vetor o que não é permitido pois o vetor não possui eta posição.

Um outro ponto importante é que uma array não pode ser redimensionado. Uma vez criado ele manterá sempre o seu tamanho. Isto pode ser contornado através da criação de um novo array de tamanho maior ou menor e, então, copiando devidamente os elementos do array original.

Exemplo 2

Vamos a um problema um pouco mais complexo: Implementar um programa Java para ler 5 notas inteirass para um vetor e imprimir a quantidade (o número) de números acima da média.

DICA: Definir um contador, iniciado em zero. Ler os 5 números para um vetor e calcular a media. Fazer um segundo loop sobre o vetor testando cada item para verificar se é maior que a média. Incrementar o contador a cada item acima da média.

Este problema exige que os dados sejam armazenados previamente, de forma que a média calculada possa posteriormente ser usada para a computação da quantidade de números que estão acima da média.

DADOS DE ENTRADA: 5 números a serem fornecidos.
DADOS DE SAÍDA: Quantidade de números acima da média.
import java.util.Scanner;

public class NumerosAcimaMedia {
    public static void main(String[] args) {
        int[]  TabelaNotas = new int[5];
        Scanner teclado = new Scanner(System.in);
        double media;
        int somaAc, contAcima;

        for (int i = 0; i < 5; i++) {
            System.out.println("Entre com a nota do aluno " + i);
            TabelaNotas[i] = teclado.nextInt();
        }     
        /* os números já estão no vetor... Vamos calcular a média deles... */
        somaAc = 0;
        for (int i = 0; i < 5; i++) {
            somaAc += TabelaNotas[i];
        }          
        media = (double) somaAc / 5; 
        System.out.println("Media calculada é " + media); 
        /* agora vamos olhar para cada elemento do vetor e contar as ocorrências acima da média */  
        contAcima = 0;
        for (int i = 0; i < 5; i++) {
            if ( TabelaNotas[i] > media )
                contAcima++;
        }    
        System.out.println("Notas acima da média: " + contAcima);         
    }
}

Exercício 1

Modificar o exemplo anterior para que seja calculada a média das notas notas abaixo de 6.

Exercício 2

Modificar o exemplo anterior para que seja calculado o desvio padrão da turma. Ver o que é desvio padrão aqui. OBS: Será necessário usar a função de raiz quadrada e de potenciação da biblioteca matemática.

Inicialização de um Vetor

Um vetor pode ser inicializado com valores já na sua criação:

 String tabelaNomes[] = new String[] {"IFSC", "IFRS", "IFRN"};

A dimensão da tavelaNomes será 3 neste caso, pois não fornecido o tamanho do vetor.

EXEMPLO 3 - Controle de Acesso

Um sistema de controle de acesso permite armazenar até 5 senhas alfanuméricas. De fábrica o produto vem com uma tabela já predefinida da forma:

 String[] tabelaSenhas  = new String[]{"alfa","beta","delta","gama", "epson"};

Elaborar um programa Java para abrir a porta para um usuário cuja senha está na tabela.

import java.util.Scanner;

public class ControleAcesso {
    public static void main(String[] args) {
        String[] tabelaSenhas  = new String[]{"alfa","beta","delta","gama", "epson"};
        Scanner teclado = new Scanner(System.in);
        String senhaLida;
        boolean itemEncontrado;

        while (true) {
            System.out.println("Entre com uma senha");
            senhaLida = teclado.nextLine();
            itemEncontrado = false;
            for (  int i = 0; i < tabelaSenhas.length; i++) {
                if (tabelaSenhas[i].equals(senhaLida)) {
                    System.out.println("Abrir a porta");
                    itemEncontrado = true;
                    break;
                }
            }
            if ( itemEncontrado == false ) {
                System.out.println("Usuário não reconhecido!!!");
            }
        }
    }
}

Exercício 3

Modifique o exemplo anterior para que exista também um USERID por cada senha. Para tanto, faça uma estratégia que cada linha para da tabela armazene o USERID e a na sequência a senha. OBS: Esta talvez não seja a melhor solução para este tipo de problema, como será visto nas UCs que se seguem.

Formas de "iterar" um array

O termo ITERAÇÃO é usado em computação para denotar a repetição de uma mesma ação sobre diferentes elementos de um dado conjunto.

No exemplo acima, os elementos do vetor foram "iterados" usando um comando for da forma:

            for (  int i = 0; i < tabelaSenhas.length; i++) {
                if (tabelaSenhas[i].equals(senhaLida)) {
                    System.out.println("Abrir a porta");
                    itemEncontrado = true;
                    break;
                }
            }

Esta abordagem pode ser usada também com comandos while() e do while().

É possível também usar a seguinte variação na iteração, conhecida como "for each" (para cada):

            for (  String senha : tabelaSenhas ) {
                if ( senha.equals(senhaLida) ) {
                    System.out.println("Abrir a porta");
                    itemEncontrado = true;
                    break;
                }
            }

Notar aqui que o comando for não contém as tradicionais três partes separadas por ponto-e-vírgula. É implícito que a cada laço do loop a variável senha assumirá o próximo valor armazenado no vetor. É importante observar que esta forma de acessar pode não ser a mais conveniente em determinadas situações que exijam, por exemplo, modificar um item de um array. Na abordagem de armazenamento de USERID seguido de SENHA no exemplo de controle de acesso também pode ser necessário um processamento extra.

Passando um vetor como parâmetro de um método

Observe a seguir o método mediaVetor(), que tem como parâmetro um vetor cuja média será computada e retornada:

public class MediaVetor {
    public static double mediaVetor(int[] meuVetor) {
        int somaAc = 0;
        for ( int x : meuVetor) {
            somaAc += x;
        }
        return (double) somaAc / meuVetor.length;
    }
    public static void main(String[] args) {
        int[] vetorTeste1 = new int[]{10, 20, 5, 10};

        System.out.println("Média do vetor teste 1 é " + mediaVetor(vetorTeste1));
    }
}

Aparentemente, a sintaxe usada segue o que foi visto até agora. Entretanto, existe uma diferença semântica: arrays são passados como REFERÊNCIA. Isto quer dizer que se o vetor for modificado dentro do método, o vetor original será modificado. Ou seja, ao contrário de parãmetros inteiros, double etc., que recebem cópias dos valores na chamada do método, arranjos são passados por endereço.

EXEMPLO 4

public class MediaVetorZerando {
    public static double mediaVetorZerando(int[] meuVetor) {
        int somaAc = 0, i;

        i = 0;
        for ( int x : meuVetor) {
            somaAc += x;
            meuVetor[i] = 0;
            i++;
        }

        return (double) somaAc / meuVetor.length;
    }
    public static void main(String[] args) {
        int[] vetorTeste1 = new int[]{10, 20, 5, 10};

        System.out.println("Média do vetor teste 1 é " + mediaVetorZerando(vetorTeste1));
        for ( int x : vetorTeste1) {
            System.out.println(x);
        }        
    }
}

EXEMPLO 1

Implementar um método que recebe um arranjo de inteiros como parâmetro. O método deve zerar todos os elementos negativos. O A quantidade de elementos zerados deve ser retornada.

import java.util.Arrays;
public class SubstituirNegativos {

    public static int substituirNegativosPorZero(double[] array) {
        int countZeros = 0;
        for (int i = 0; i < array.length; i++) {
            if (array[i] < 0) {
                array[i] = 0;
                countZeros++;
            }
        }
        return countZeros;
    }

    public static void main(String[] args) {
        double[] numeros = {2.5, -1.3, 4.8, -3.0, 5.7};

        System.out.println("Array original: " + Arrays.toString(numeros));

        int quantidadeZeros = substituirNegativosPorZero(numeros);

        System.out.println("Array modificado: " + Arrays.toString(numeros));
        System.out.println("Quantidade de elementos substituídos por zero: " + quantidadeZeros);
    }
}


Exercício 4

Implementar um método capaz de retornar o index de um elemento de um array de strings que possui a primeira ocorrência de uma string também passada como parâmetro. O método deve retornar -1 caso não encontre uma ocorrência.

public class RetornoIndexArray {
    public static int indexArray(String[] vetorStrings, String stringProcurada) {
        /* implementar a busca aqui  */

        return -1;
    }
    public static void main(String[] args) {
        String[] teste = new String[] {"alfa", "beta", "gamma", "delta"};
        int retorno;

        retorno = indexArray(teste, "gamma");
        System.out.println("Index retornado é " + retorno);

    }
}

Exercício 5

Implementar um método em java que recebe um arranjo de inteiros e retorna a quantidade de números pares que estão no arranjo. Teste no método main() chamando pelo menos duas vezes.

Exercício 6

Implementar um método java que recebe um arranjo de inteiros como parâmetro e retorna true se os números estão em ordem crescente ou false se não estão. Testar chamando pelo menos duas vezes no método main().

Retorno de Array em Métodos

Um ponto importante é que é possível retornar um array (por referência) em um método. Vamos a um exemplo.

EXEMPLO 1

Implementar um método que recebe 2 arrays de double de mesma dimensão como parâmetros e retorna um array contendo a soma dos mesmos.

public class SomaArrays {

    public static double[] somarrArrays(double[] array1, double[] array2) {
        /* Aqui seria interessante fazer um tratamento de exceção para o caso de dimensões diferentes */

        double[] resultado = new double[array1.length];
        for (int i = 0; i < array1.length; i++) {
            resultado[i] = array1[i] + array2[i];
        }
        return resultado;
    }

    public static void main(String[] args) {
        double[] array1 = {1.5, 2.0, 3.5, 4.0, 5.5};
        double[] array2 = {2.0, 1.5, 2.5, 2.0, 1.5};
        
        double[] resultado = somarArrays(array1, array2);

        System.out.print("Resultado da soma: ");
        for (double elemento : resultado) {
            System.out.print(elemento + " ");
        }
    }
}

Exercício

Implementar um método que recebe um array de strings e retorna um array com as strings capitalizadas. Use o método toUpperCase() associado a uma string conforme exemplo abaixo:

public class CapitalizarString {

    public static void main(String[] args) {
        String texto = "exemplo de string";
        String capitalizado = texto.toUpperCase();

        System.out.println("Original: " + texto);
        System.out.println("Capitalizado: " + capitalizado);
    }
}

Explorando o lado da criação dinâmica de arranjos

Observe que a criação "dinãmica" de arranjos simplifica determinadas soluções de problemas que exigem flexibilidade na criação de uma estrutura de dados cujo tamanho não é conhecido previamente à execução do programa.

import java.util.Scanner;

public class TabelaNotas {
    public static void main(String[] args) {
        int[]  TabelaNotas;
        Scanner teclado = new Scanner(System.in);

        System.out.println("Entre com o tamanho da turma");
        int tamanhoTurma = teclado.nextInt();
        TabelaNotas = new int[tamanhoTurma];

        for (int i = 0; i < TabelaNotas.length; i++) {
            System.out.println("Entre com a nota do aluno " + i);
            TabelaNotas[i] = teclado.nextInt();
        }
        System.out.println("Dados armazenados!!!");
        /* imprimindo os dados lidos... */
        for (int i = 0; i < TabelaNotas.length; i++) {
            System.out.println("Nota da posição " + i + " é " + TabelaNotas[i]);
        }
        teclado.close();
    }
}

Observe neste exemplo que a TabelaNotas é criada com a dimensão dos dados a serem armazenados:

    TabelaNotas = new int[tamanhoTurma];


Execício