Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Junte-se a nós no Amazon Web Services re:Invent 2024! Saiba como usar o MongoDB para casos de uso de AI .
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
JavaScriptchevron-right

Uma leve introdução às listas vinculadas com o MongoDB

Joe Karlsson13 min read • Published Jan 18, 2022 • Updated Apr 02, 2024
MongoDBJavaScript
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Você é novo em estruturas de dados e algoritmos? Neste post, você conhecerá uma das estruturas de dados mais importantes da Ciência da Computação, a Lista Vinculada, implementada com um toque do MongoDB. Esta postagem cobrirá os fundamentos da estrutura de dados da lista vinculada. Ele também responderá a perguntas como: "Como as listas vinculadas diferem das matrizes?" e "Quais são os prós e os contras de usar uma lista vinculada?"

Introdução às listas vinculadas

Você sabia que as listas vinculadas são uma das estruturas de dados fundamentais da ciência da computação? Se você é como muitos desenvolvedores autodidatas ou se formou em um campo de treinamento de desenvolvedores, talvez precise de uma pequena lição sobre como essa estrutura de dados funciona. Ou, se você for como eu, pode precisar de uma atualização se já se passaram alguns anos desde sua última palestra de Ciência da Computação sobre estruturas de dados e algoritmos. Neste post, mostrarei como implementar uma lista vinculada do zero usando o Node.js e o MongoDB. Este também é um ótimo lugar para começar a lidar com os fundamentos das operações CRUD do MongoDB e essa estrutura de dados histórica. Vamos começar com o básico.
Diagrama de uma lista vinculada individualmente
Diagrama de uma lista vinculada individualmente
Uma lista vinculada é uma estrutura de dados que contém uma lista de nós conectados por meio de referências ou ponteiros. Um nó é um objeto na memória. Geralmente contém no máximo duas informações, um valor de dados e um ponteiro para o próximo nó na lista vinculada. As listas vinculadas também possuem referências de ponteiro separadas para o início e o fim da lista vinculada. A cabeça é o primeiro nó da lista, enquanto a cauda é o último objeto da lista.
Um nó que NÃO se vincula a outro nó
1{
2 "data": "Cat",
3 "next": null
4}
Um nó que faz um link para outro nó
1{
2 "data": "Cat",
3 "next": {
4 "data": "Dog",
5 "next": {
6 "data": "Bird",
7 "next": null
8 }
9 } // these are really a reference to an object in memory
10}

Por que usar uma lista encadeada?

Há vários motivos pelos quais as listas vinculadas são usadas, em vez de outras estruturas de dados, como matrizes (mais sobre isso adiante). No entanto, usamos listas vinculadas em situações em que não sabemos o tamanho exato da estrutura de dados, mas prevemos que a lista possa crescer para tamanhos grandes. Muitas vezes, as listas vinculadas são usadas quando pensamos que a estrutura de dados pode ficar maior do que a memória disponível do computador com o qual estamos trabalhando. As listas vinculadas também são úteis se ainda precisarmos preservar a ordem e prever que a ordem mudará com o tempo.
As listas vinculadas são apenas objetos na memória. Um objeto contém uma referência a outro objeto ou um nó contém um ponteiro para o próximo nó. Na memória, uma lista vinculada tem a seguinte aparência:
Diagrama que demonstra como as listas vinculadas alocam ponteiros de uso para vincular dados na memória
Diagrama que demonstra como as listas vinculadas alocam ponteiros de uso para vincular dados na memória

Benefícios das listas vinculadas

  • As listas vinculadas são dinâmicas por natureza, o que aloca a memória quando necessário.
  • As operações de inserção e exclusão podem ser facilmente implementadas.
  • Pilhas e filas podem ser facilmente executadas usando uma lista vinculada.

Desvantagens das Listas Vinculadas

  • A memória é desperdiçada, pois os ponteiros exigem memória extra para armazenamento.
  • Nenhum elemento pode ser acessado aleatoriamente; ele tem que acessar cada nó sequencialmente a partir da cabeça.
  • A travessia reversa é difícil em uma lista vinculada apenas.

Comparação entre arrays e listas vinculadas

Agora, você pode estar achando que as listas vinculadas se parecem muito com arrays, e você está correto! Ambos mantêm o controle de uma sequência de dados e podem ser iterados e repetidos em loop. Além disso, ambas as estruturas de dados preservam a ordem da sequência. No entanto, existem algumas diferenças importantes.

Vantagens de Arrays

  • As matrizes são simples e fáceis de usar.
  • Oferecem acesso mais rápido aos elementos (O(1) ou tempo constante).
  • Eles podem acessar elementos por qualquer índice sem precisar iterar por todo o conjunto de dados desde o início.

Desvantagens de Arrays

  • Você estava ciente de que as arrays podem desperdiçar memória? Isso ocorre porque normalmente os compiladores pré-alocam um bloco sequencial de memória quando uma nova array é criada para fazer queries super rápidas. Portanto, muitos desses blocos de memória pré-alocados podem estar vazios.
  • As arrays têm um tamanho fixo. Se o bloco de memória pré-alocado estiver cheio até a capacidade máxima, o compilador de código alocará um bloco de memória ainda maior e precisará copiar a matriz antiga para o novo bloco de memória da matriz antes que novas operações de matriz possam ser executadas. Isso pode ser caro com tempo e espaço.
Diagrama que demonstra como os arrays alocam blocos contínuos de espaço de memória
Diagrama que demonstra como os arrays alocam blocos contínuos de espaço de memória
Diagrama que demonstra como as listas vinculadas alocam memória para novos nós da lista vinculada
Diagrama que demonstra como as listas vinculadas alocam memória para novos nós da lista vinculada
  • Para inserir um elemento em uma determinada posição, a operação é complexa. Talvez seja necessário mudar os elementos existentes para criar uma vaga e inserir o novo elemento na posição desejada.

Outros tipos de listas vinculadas

Lista duplamente vinculada

Uma lista duplamente vinculada é o mesmo que uma lista vinculada simples, com a exceção de que cada nó também aponta para o nó anterior, bem como para o próximo nó.
Diagrama de uma lista duplamente vinculada
Diagrama de uma lista duplamente vinculada

Lista vinculada circular

Uma lista circular vinculada é igual a uma lista vinculada individualmente, com a exceção de que não há conceito de cabeça ou cauda. Todos os nós apontam para o próximo nó circularmente. Não há um início verdadeiro para a lista vinculada circular.
Diagrama de uma lista vinculada circular
Diagrama de uma lista vinculada circular

Vamos codificar uma lista vinculada!

Primeiro, vamos configurar nosso ambiente de codificação

Criando um cluster no Atlas

A primeira coisa que precisaremos configurar é uma contaMongoDB Atlas. E não se preocupar, você pode criar um cluster M0 MongoDB Atlas gratuitamente. Não é necessário nenhum cartão de crédito para começar! Para entrar em operação com um cluster M0 gratuito, siga o guia de Começando.
Depois de nos inscrevermos no Atlas, precisaremos implantar um cluster MongoDB gratuito. Observe que você também precisará adicionar uma regra para permitir o endereço IP do computador ao qual estamos conectando o MongoDB Atlas Custer e precisará criar um usuário do banco de dados antes de poder se conectar ao novo cluster. Esses são recursos de segurança implementados para garantir que atores mal-intencionados não possam acessar seu banco de dados.
Se você tiver algum problema ao conectar ou configurar seu MongoDB Atlas cluster gratuito, não deixe de conferir os MongoDB Community para obter ajuda.

Conecte-se ao plug-in MongoDB do VS Code

Em seguida, vamos nos conectar ao nosso novo cluster de banco de dados MongoDB Atlas usando o plug-in MongoDB do Visual Studio Code. A extensão MongoDB nos permite:
  • Conecte-se a um cluster do MongoDB ou Atlas, navegue pelos seus bancos de dados e coleções, obtenha uma visão geral rápida do seu esquema e veja os documentos em suas coleções.
  • Crie o MongoDB Playgrounds, a maneira mais rápida de criar protótipos de operações CRUD e comandos do MongoDB.
  • Acesse rapidamente o MongoDB Shell para iniciar o MongoDB Shell a partir da paleta de comandos e conecte-se rapidamente ao cluster ativo.
Para instalar MongoDB for VS Code, basta procurar por ele na lista de extensões diretamente dentro VS Code ou ir para a página inicial "MongoDB for VS Code" no VS Code Marketplace.
MongoDB for VS Code pode se conectar a instâncias ou clusters autônomos do MongoDB no MongoDB Atlas ou auto-hospedado. Uma vez conectado, você pode procurar bancos de dados, collectionse visualizações somente leitura diretamente da visualização em árvore.
Para cada collection, você verá uma lista de documentos de amostra e uma visão geral rápida do esquema. Isso é muito útil como referência ao escrever consultas e agregações.
Uma vez instalado, haverá uma nova guia MongoDB que podemos usar para adicionar nossas conexões clicando em "Adicionar conexão". Se você já usou o MongoDB Compass antes, o formulário deve ser familiar. Você pode inserir seus detalhes de conexão no formulário ou usar uma string de conexão. Escolhi o último, pois meu banco de dados está hospedado no MongoDB Atlas.
Para obter sua connection string, navegue até a página "Clusters" e selecione "Connect".
Escolha a opção "Conectar-se usando o MongoDB Compass" e copie a connection string. Adicione seu nome de usuário e senha em seus respectivos locais antes de inserir a string no VS Code.
Depois de se conectar com sucesso, você deverá ver um alerta. Neste ponto, você pode explorar os dados em seu cluster, bem como seus esquemas.

Criando funções para inicializar o aplicativo

Tudo bem, agora que conseguimos nos conectar ao nosso banco de dados MongoDB Atlas, vamos escrever um código para permitir que nossa lista vinculada se conecte ao nosso banco de dados e faça algumas limpezas enquanto desenvolvemos nossa lista vinculada.
A estratégia geral para criar nossas listas vinculadas com o MongoDB será a seguinte. Usaremos um documento do MongoDB para manter o controle das metainformações, como a localização da cabeça e da cauda. Também usaremos um documento exclusivo do MongoDB para cada nó em nossa lista vinculada. Usaremos os IDs exclusivos que são gerados automaticamente pelo MongoDB para simular um ponteiro. Portanto, o próximo valor de cada nó da lista vinculada armazenará o ID do próximo nó da lista vinculada. Dessa forma, poderemos iterar em nossa lista vinculada.
Então, para conseguir isso, a primeira coisa que vamos fazer é configurar nossa classe de lista vinculada.
1const MongoClient = require("mongodb").MongoClient;
2
3// Define a new Linked List class
4class LinkedList {
5
6 constructor() {}
7
8 // Since the constructor cannot be an asynchronous function,
9 // we are going to create an async `init` function that connects to our MongoDB
10 // database.
11 // Note: You will need to replace the URI here with the one
12 // you get from your MongoDB Cluster. This is the same URI
13 // that you used to connect the MongoDB VS Code plugin to our cluster.
14 async init() {
15 const uri = "PASTE YOUR ATLAS CLUSTER URL HERE";
16 this.client = new MongoClient(uri, {
17 useNewUrlParser: true,
18 useUnifiedTopology: true,
19 });
20
21 try {
22 await this.client.connect();
23 console.log("Connected correctly to server");
24 this.col = this.client
25 .db("YOUR DATABASE NAME HERE")
26 .collection("YOUR COLLECTION NAME HERE");
27 } catch (err) {
28 console.log(err.stack);
29 }
30 }
31}
32
33// We are going to create an immediately invoked function expression (IFEE)
34// in order for us to immediately test and run the linked list class defined above.
35(async function () {
36 try {
37 const linkedList = new LinkedList();
38 await linkedList.init();
39 linkedList.resetMeta();
40 linkedList.resetData();
41 } catch (err) {
42 // Good programmers always handle their errors
43 console.log(err.stack);
44 }
45})();
Em seguida, vamos criar algumas funções auxiliares para redefinir nosso banco de dados toda vez que executarmos o código, de modo que nossos dados não fiquem cheios de dados antigos.
1// This function will be responsible for cleaning up our metadata
2// function everytime we reinitialize our app.
3async resetMeta() {
4 await this.col.updateOne(
5 { meta: true },
6 { $set: { head: null, tail: null } },
7 { upsert: true }
8 );
9}
10
11// Function to clean up all our Linked List data
12async resetData() {
13 await this.col.deleteMany({ value: { $exists: true } });
14}
Agora, vamos escrever algumas funções auxiliares para nos ajudar a consultar e atualizar nosso documento meta.
1// This function will query our collection for our single
2// meta data document. This document will be responsible
3// for tracking the location of the head and tail documents
4// in our Linked List.
5async getMeta() {
6 const meta = await this.col.find({ meta: true }).next();
7 return meta;
8}
9
10// points to our head
11async getHeadID() {
12 const meta = await this.getMeta();
13 return meta.head;
14}
15
16// Function allows us to update our head in the
17// event that the head is changed
18async setHead(id) {
19 const result = await this.col.updateOne(
20 { meta: true },
21 { $set: { head: id } }
22 );
23 return result;
24}
25
26// points to our tail
27async getTail(data) {
28 const meta = await this.getMeta();
29 return meta.tail;
30}
31
32// Function allows us to update our tail in the
33// event that the tail is changed
34async setTail(id) {
35 const result = await this.col.updateOne(
36 { meta: true },
37 { $set: { tail: id } }
38 );
39 return result;
40}
41
42// Create a brand new linked list node
43async newNode(value) {
44 const newNode = await this.col.insertOne({ value, next: null });
45 return newNode;
46}

Add A Node

As etapas para adicionar um novo nó a uma lista vinculada são:
  • Adicionar um novo nó à cauda atual.
  • Atualize as caudas atuais ao lado do novo nó.
  • Atualize sua lista vinculada para apontar para o novo nó.
1// Takes a new node and adds it to our linked lis
2async add(value) {
3 const result = await this.newNode(value);
4 const insertedId = result.insertedId;
5
6 // If the linked list is empty, we need to initialize an empty linked list
7 const head = await this.getHeadID();
8 if (head === null) {
9 this.setHead(insertedId);
10 } else {
11 // if it's not empty, update the current tail's next to the new node
12 const tailID = await this.getTail();
13 await this.col.updateOne({ _id: tailID }, { $set: { next: insertedId } });
14 }
15 // Update your linked list to point tail to the new node
16 this.setTail(insertedId);
17 return result;
18}

Encontrar um nó

Para percorrer uma lista vinculada, devemos começar no início da lista vinculada, também conhecida como cabeça. Em seguida, seguimos cada referência de ponteiroseguinte até chegarmos ao final da lista vinculada, ou ao nó que estamos procurando. Isso pode ser implementado usando as seguintes etapas:
  • Comece pelo nó principal da sua lista vinculada.
  • Verifique se o valor corresponde ao que você está procurando. Se encontrado, retorna esse nó.
  • Se não for encontrado, passe para o próximo nó por meio da propriedade next do nó atual.
  • Repita até que o próximo seja nulo (final/fim da lista).
1// Reads through our list and returns the node we are looking for
2async get(index) {
3 // If index is less than 0, return false
4 if (index <= -1) {
5 return false;
6 }
7 let headID = await this.getHeadID();
8 let postion = 0;
9 let currNode = await this.col.find({ _id: headID }).next();
10
11 // Loop through the nodes starting from the head
12 while (postion < index) {
13 // Check if we hit the end of the linked list
14 if (currNode.next === null) {
15 return false;
16 }
17
18 // If another node exists go to next node
19 currNode = await this.col.find({ _id: currNode.next }).next();
20 postion++;
21 }
22 return currNode;
23}

Excluir um nó

Agora, digamos que queremos remover um nó em nossa lista encadeada. Para fazer isso, devemos acompanhar novamente o nó anterior para que possamos atualizar a próxima referência de ponteiro do nó anterior para o nó que está sendo excluído para o qual o próximo valor está apontando. Ou, dito de outra forma:
  • Encontre o nó que você está procurando e acompanhe o nó anterior.
  • Quando encontrado, atualize os nós anteriores a seguir para apontar para o próximo nó referenciado pelo nó a ser excluído.
  • Exclui da memória o nó encontrado.
Diagrama que demonstra como as listas vinculadas removem um nó de uma lista vinculada movendo referências de ponteiro
Diagrama que demonstra como as listas vinculadas removem um nó de uma lista vinculada movendo referências de ponteiro
1// reads through our list and removes desired node in the linked list
2async remove(index) {
3 const currNode = await this.get(index);
4 const prevNode = await this.get(index - 1);
5
6 // If index not in linked list, return false
7 if (currNode === false) {
8 return false;
9 }
10
11 // If removing the head, reassign the head to the next node
12 if (index === 0) {
13 await this.setHead(currNode.next);
14
15 // If removing the tail, reassign the tail to the prevNode
16 } else if (currNode.next === null) {
17 await this.setTail(prevNode._id);
18 await this.col.updateOne(
19 { _id: prevNode._id },
20 { $set: { next: currNode.next } }
21 );
22
23 // update previous node's next to point to the next node referenced by node to be deleted
24 } else {
25 await this.col.updateOne(
26 { _id: prevNode._id },
27 { $set: { next: currNode.next } }
28 );
29 }
30
31 // Delete found node from memory
32 await this.col.deleteOne({
33 _id: currNode._id,
34 });
35
36 return true;
37}

Inserir um nó

O código a seguir insere um nó após um nó existente em uma lista vinculada individualmente. A inserção de um novo nó antes de um existente não pode ser feita diretamente; em vez disso, deve-se acompanhar o nó anterior e inserir um novo nó depois dele. Podemos fazer isso seguindo estas etapas:
  • Encontre a posição/nó em sua lista vinculada onde você deseja inserir seu novo nó depois.
  • Atualize a próxima propriedade do novo nó para apontar para o nó para o qual o nó de destino aponta atualmente.
  • Atualize a próxima propriedade do nó que você deseja inserir depois para apontar para o novo nó.
Diagrama que demonstra como uma lista vinculada insere um novo nó movendo referências de ponteiro
Diagrama que demonstra como uma lista vinculada insere um novo nó movendo referências de ponteiro
1// Inserts a new node at the deisred index in the linked list
2async insert(value, index) {
3 const currNode = await this.get(index);
4 const prevNode = await this.get(index - 1);
5 const result = await this.newNode(value);
6 const node = result.ops[0];
7
8 // If the index is not in the linked list, return false
9 if (currNode === false) {
10 return false;
11 }
12
13 // If inserting at the head, reassign the head to the new node
14 if (index === 0) {
15 await this.setHead(node._id);
16 await this.col.updateOne(
17 { _id: node._id },
18 { $set: { next: currNode.next } }
19 );
20 } else {
21 // If inserting at the tail, reassign the tail
22 if (currNode.next === null) {
23 await this.setTail(node._id);
24 }
25
26 // Update the next property of the new node
27 // to point to the node that the target node currently points to
28 await this.col.updateOne(
29 { _id: prevNode._id },
30 { $set: { next: node._id } }
31 );
32
33 // Update the next property of the node you
34 // want to insert after to point to the new node
35 await this.col.updateOne(
36 { _id: node._id },
37 { $set: { next: currNode.next } }
38 );
39 }
40 return node;
41}

Resumo

Muitos desenvolvedores querem aprender as estruturas de dados e os algoritmos fundamentais da ciência da computação ou se atualizar sobre eles. Na humilde opinião deste autor, a melhor maneira de aprender estruturas de dados é implementando-as por conta própria. Este exercício é uma ótima maneira de aprender estruturas de dados, bem como aprender os fundamentos das operações CRUD do MongoDB.
Quando estiver pronto para implementar sua própria lista vinculada no MongoDB, confira o MongoDB Atlas, o banco de dados como serviço totalmente gerenciado do MongoDB. O Atlas é a maneira mais fácil de começar a usar o MongoDB e tem uma camada rica e gratuita para sempre.
Se você quiser saber mais sobre listas vinculadas e MongoDB, não deixe de conferir esses recursos.
Confira os seguintes recursos para obter mais informações:

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Artigo

Criando aplicativos de remix com a pilha MongoDB


Apr 02, 2024 | 4 min read
Artigo

AI Shop: o poder da LangChain, OpenAI e MongoDB Atlas trabalhando juntos


Sep 18, 2024 | 7 min read
Tutorial

Comece a usar o MongoDB Atlas sem servidor, AWS CDK e AWS sem servidor


Aug 09, 2024 | 18 min read
Artigo

Crie um site de boletim informativo com a plataforma de dados MongoDB


Sep 09, 2024 | 9 min read
Sumário