• Nenhum resultado encontrado

TÓPICO 1 – LISTAS ENCADEADAS

2.3 IMPLEMENTAÇÃO DOS MÉTODOS

Aqui vamos começar a aumentar o código da nossa classe ListaEncadeada baseado nas operações que precisamos realizar com ela. Para facilitar a contagem de elementos (pessoas) e algumas operações, logo a seguir dos atributos primeiro e último, adicione o atributo int totalDeElementos, perceba.

FONTE: O autor

2.3.1 Método adicionaNoComeco()

Inserir no começo da Lista é muito simples, basta criarmos um novo nó, que se dará através da utilização do operador new, que segundo Deitel e Deitel (2003) é essencial para alocação dinâmica na memória, já que recebe como operando o tipo do objeto que está sendo dinamicamente alocado e devolve uma referência para um objeto desse tipo criado. Além disso, este objeto recém-criado terá a referência “proximo" apontando para o atual “primeiro” da lista,

Neste momento, você deve se perguntar: por que não foi definido o método adicionaNoFim()? A explicação é simples: O método “adiciona()” já definido anteriormente (Figura 88), exercerá a função de inserir no fim da Lista, não havendo desta forma, a necessidade de criar um novo método para realizar esta tarefa.

FIGURA 90 – CRIAÇÃO DO ATRIBUTO PARA CONTAR OS ELEMENTOS

tanto, precisaremos de um novo atributo, que chamaremos de int totalDePessoas, e será responsável por retornar o total de pessoas que se encontram armazenadas nesta lista.

FONTE: O autor

FONTE: O Autor

FIGURA 91 – ADICIONA NO COMEÇO COM A LISTA VAZIA

FIGURA 92 – ADICIONA NO COMEÇO COM A LISTA NÃO VAZIA

Compreendido o conceito de como deve ocorrer o processo de inserção das pessoas na lista encadeada, vamos ao desenvolvimento do código-fonte no ambiente de desenvolvimento:

primeiro primeiro

null null

Lista vazia

último

Guilherme

último

primeiro primeiro

Lista não vazia

Guilherme Felipe Guilherme

FIGURA 93 – CÓDIGO PARA ADICIONAR NO COMEÇO

FONTE: O autor

2.3.2 Método adiciona()

O método adiciona() permitirá adicionar a pessoa no último nó da Lista, no entanto, se não tivéssemos guardado a referência para o último nó precisaríamos percorrer nó a nó até o fim da Lista para alterar a referência proximo do último nó, reduzindo consideravelmente o desempenho do nosso programa, já que havendo um grande número de elementos e o processo tornar-se-ia lento.

No caso especial da Lista estar vazia, adicionar no começo ou no fim dessa lista dá o mesmo efeito. Então, se a Lista estiver vazia, chamaremos o método já definido anteriormente adicionaNoComeco(Object), (CAELUM, 2014). Conforme veremos no código-fonte a seguir.

FIGURA 94 – ADICIONA NO ÚLTIMO NÓ COM A LISTA NÃO VAZIA

último último

Lista não vazia

Guilherme

Guilherme Felipe

FIGURA 95 – CÓDIGO PARA ADICIONAR NO ÚLTIMO NÓ DA LISTA

FONTE: O autor

2.3.3 Método adicionaPosicao()

A inserção no começo e no fim da Lista já foram devidamente tratadas nos itens anteriores. Aqui vamos nos preocupar em inserir em uma posição no interior da Lista, ou seja, qualquer posição que não seja a primeira e nem a última.

Para inserir um elemento em qualquer posição precisamos pegar o nó anterior ao da posição desejada, porque precisamos mexer na sua referência proximo.

(CAELUM, 2014). Para isso vamos criar um método auxiliar responsável por pegar determinado nó, ao qual atribuiremos o nome de pegaNo. Ao utilizar este método devemos tomar cuidado no caso da posição não existir. Para tanto, iremos inicialmente desenvolver o método posicaoOcupada, que verificará se a posição existe ou não.

FONTE: Caelum (2014)

Perceba que aqui estamos fazendo que haja n nós para n elementos, isto é, um nó para cada elemento. Outra implementação clássica de lista encadeada é usar um nó sentinela a mais para indicar o começo da Lista, e outro para o fim, assim poderíamos simplificar um pouco alguns desses métodos, como o caso particular de inserir um elemento quando não há ainda elemento algum. Vale sempre lembrar que aqui estamos estudando uma implementação de estrutura de dados, e que há sempre outras formas de codificá-las que podem ser mais ou menos elegantes.

FIGURA 96 – CÓDIGO PARA VERIFICAR POSIÇÃO OCUPADA

FONTE: O autor

Os métodos são privados (private), pois não queremos que ninguém de fora tenha acesso ao funcionamento interno da nossa estrutura de dados. É importante notar que o método pegaNo consome tempo linear.

Desenvolvido os métodos auxiliares, torna-se mais fácil a implementação do método adicionaPosicao(int, Object). Basta pegar o nó anterior, a posição onde a inserção será feita e atualizar as referências. O anterior deve apontar para um novo nó e o novo nó deve apontar para o antigo proximo do anterior.

(CAELUM, 2014).

Devemos tomar cuidado com os casos particulares nos quais a posição para inserir é o começo ou o fim da Lista.

FIGURA 97 – ADICIONA EM UMA POSIÇÃO DA LISTA

Lista com 2 elementos

FIGURA 98 – CÓDIGO PARA ADICIONAR EM UMA POSIÇÃO DA LISTA

FONTE: O autor

2.3.4 Método pega()

Para pegar um elemento é muito simples: basta pegarmos o nó em que aquele elemento se encontra e acessar o elemento que se encontra dentro dele.

Podemos utilizar o método pegaNo(int) previamente criado:

FONTE: O autor

Perceba que este método consome tempo linear. Esta é uma grande desvantagem da Lista Encadeada em relação aos Vetores. Vetores possuem o chamado acesso aleatório aos elementos, ou seja, qualquer posição pode ser acessada em tempo constante. Apesar dessa grande desvantagem, diversas vezes utilizamos uma Lista e não é necessário ficar acessando posições aleatórias: comumente percorremos a lista por completa, que veremos como fazer mais adiante.

FONTE: O autor

2.3.5 Método removeDoComeco()

FIGURA 99 – CÓDIGO PARA PEGAR UM ELEMENTO NA LISTA

Antes de tentar remover devemos verificar se a posição está ocupada.

Não faz sentido remover algo que não existe. Depois, basta “avançar” a referência que aponta para o primeiro nó.

Por fim, é importante perceber que a Lista pode ficar vazia. Neste caso, devemos colocar null na referência que aponta para o último nó.

Se não fizermos isso ficaríamos em um estado inconsistente, em que o atributo primeiro é null e o último não, ou seja, tem um último, mas não tem um primeiro, algo que não faria sentido.

FIGURA 100 – REMOVE DO COMEÇO – LISTA COM APENAS UM ELEMENTO

FONTE: O autor

FIGURA 101 – REMOVE DO COMEÇO – LISTA COM PELO MENOS DOIS (2) ELEMENTOS

Guilherme

Lista com apenas um elemento

primeiro primeiro

null null

último último

primeiro primeiro

FIGURA 102 – CÓDIGO PARA REMOVER DO COMEÇO DA LISTA

FONTE: O autor

2.3.6 Método removeDoFim()

A primeira verificação a ser realizada tem por objetivo constatar se a última posição existe, que poderá ser realizada através do método anteriormente criado posicaoOcupada(int).

Ao constatar que a Lista possui apenas um elemento o processo de remoção do fim será idêntico a remover do começo, possibilitando desta forma a reutilização do método removeDoComeco(), nestes casos.

No entanto, se a Lista possui mais que um elemento, devemos pegar o penúltimo nó, fazer o próximo do penúltimo ser null e fazer o atributo ultimo apontar para o penúltimo.

A questão neste momento é: como pegar o penúltimo nó? Podemos fazer isso usando o método pegaNo(int), mas isso consumiria tempo linear, reduzindo consideravelmente o desempenho de nosso programa. Desta forma, como queremos consumo constate teremos que achar outra solução.

Então, em vez de fazer uma Lista Encadeada simples, vamos fazer uma Lista Duplamente Encadeada. Ou seja, cada nó aponta para o seu anterior além de apontar para a próxima. (CAELUM, 2014). Para tanto, devemos reabrir a classe No e adicionar o código a seguir.

FIGURA 103 – CÓDIGO PARA IMPLEMENTAR A LISTA DUPLAMENTE ENCADEADA

FONTE: O autor

FIGURA 104 – REMOVE DO FIM – LISTA COM PELO MENOS DOIS (2) ELEMENTOS

FONTE: O autor

Com cada nó sabendo quem é o seu anterior, fica fácil escrever o método removeDoFim().

Lista com pelo menos 2 elementos

último último

Guilherme Felipe Guilherme

FIGURA 105 – CÓDIGO PARA REMOVER DO FIM DA LISTA

FONTE: O autor

2.3.7 Método remove()

O respectivo método possibilitará a remoção do elemento de qualquer posição da Lista. Contudo, inicialmente devemos verificar se a posição está ou não ocupada. Se não estiver ocupada, devemos lançar uma exceção, caso contrário, devemos verificar se a remoção é do começo ou do fim da Lista, haja vista que se for um destes casos, basta chamarmos os métodos já implementados removeDoComeco ou removeDoFim.

Por fim, se a remoção é no interior da Lista, devemos atualizar as referências dos nós relacionados ao nó que vamos remover (anterior e próximo). O próximo do anterior deve ser o proximo e o anterior do proximo deve ser o anterior.

A modificação para Lista Duplamente Encadeada implicará pequenas modificações nos outros métodos que já tínhamos implementado, que serão apresentadas no Tópico a seguir.

FIGURA 106 – REMOVE DO INTERIOR DA LISTA

FONTE: O autor

FONTE: O autor

2.3.8 Método contem()

Está operação deve percorrer a Lista e comparar com o método equals(Object) o elemento procurado contra todos os elementos da Lista.

FIGURA 107 – CÓDIGO PARA REMOVER DO INTERIOR DA LISTA

Guilherme Felipe Letícia Guilherme Letícia

Lista com pelo menos 3 elementos

FIGURA 108 – CÓDIGO PARA VERIFICAR SE UM ELEMENTO ESTÁ NA LISTA

FONTE: O autor

2.3.9 Método tamanho()

Está operação não tem segredo, pois já definimos um atributo que possui esta informação, qual seja totalDeElementos.

FONTE: O autor