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