Em Python, a escolha da estrutura de dados correta é fundamental para escrever código eficiente, legível e de fácil manutenção. Cada estrutura — listas, tuplas, sets e dicionários — possui características únicas que as tornam mais adequadas para determinados cenários. Nesta aula, vamos explorar as diferenças entre elas, analisar a complexidade de tempo e espaço de suas operações, discutir casos de uso típicos e comparar a performance. Ao final, você será capaz de selecionar a estrutura mais apropriada para cada problema.

Lista vs tupla vs set vs dict

Vamos revisar rapidamente as características fundamentais de cada estrutura:

  • Lista: sequência mutável, ordenada, indexada. Permite elementos duplicados. Sintaxe: [1, 2, 3].
  • Tupla: sequência imutável, ordenada, indexada. Permite elementos duplicados. Sintaxe: (1, 2, 3).
  • Set: coleção não ordenada, mutável, sem índices. Não permite duplicatas. Sintaxe: {1, 2, 3}.
  • Dict: mapeamento chave-valor, mutável, ordenado (a partir do Python 3.7). Chaves únicas, valores podem ser duplicados. Sintaxe: {'a': 1, 'b': 2}.

A escolha entre elas depende de fatores como necessidade de ordem, mutabilidade, unicidade e tipo de acesso (por índice, chave ou valor).

Complexidade

A complexidade de tempo (Big O) das operações comuns varia entre as estruturas. Conhecê-la ajuda a prever o desempenho em diferentes cenários.

OperaçãoListaTuplaSetDict
Acesso por índice/chaveO(1)O(1)N/AO(1) médio
Inserção no finalO(1) amortizadoN/A (imutável)O(1) médioO(1) médio
Inserção no inícioO(n)N/AN/AN/A
Remoção por valorO(n)N/AO(1) médioO(1) médio (por chave)
Busca por valorO(n)O(n)O(1) médioO(1) médio (chave)
OrdenaçãoO(n log n)O(n log n) via sorted()N/A (não ordenado)N/A (ordenação por chave via sorted())

Note que sets e dicts usam tabelas hash, proporcionando buscas rápidas, mas sem ordem garantida (embora dicts mantenham ordem de inserção desde Python 3.7). Listas e tuplas são sequências lineares, com acesso indexado rápido, mas buscas lentas.

Casos de uso

Cada estrutura se destaca em cenários específicos:

  • Lista: quando a ordem importa, você precisa de mutabilidade, ou deseja armazenar itens duplicados. Exemplos: fila de tarefas, histórico de ações, coleção de resultados.
  • Tupla: quando a imutabilidade é desejada (proteção contra alterações acidentais), ou para representar registros leves (ex.: coordenadas (x, y)). Também usada como chave de dicionário (por ser hashable).
  • Set: quando a unicidade é essencial e a ordem não importa. Ideal para testes de pertinência, remoção de duplicatas, operações de conjunto (união, interseção). Exemplo: conjunto de IDs de usuários únicos.
  • Dict: quando você precisa associar chaves a valores, como em um banco de dados em memória. Exemplos: contagem de frequências, cache, mapeamento de nomes para objetos.

Exemplo prático: suponha que você precise armazenar as notas dos alunos. Se cada aluno tem uma nota, um dict com o nome como chave e a nota como valor é ideal. Se você precisa manter a ordem de chegada, use uma lista de tuplas (nome, nota). Para garantir que não haja notas duplicadas (improvável), um set não faria sentido.

Performance

A performance prática depende do tamanho dos dados e das operações mais frequentes. Vamos comparar cenários comuns com código:

import timeit

# Cenário 1: Busca por valor em lista vs set
dados_lista = list(range(100_000))
dados_set = set(range(100_000))

tempo_lista = timeit.timeit(lambda: 99_999 in dados_lista, number=1000)
tempo_set = timeit.timeit(lambda: 99_999 in dados_set, number=1000)
print(f"Busca em lista: {tempo_lista:.5f}s")
print(f"Busca em set: {tempo_set:.5f}s")
# Resultado típico: set é muito mais rápido (O(1) vs O(n))

# Cenário 2: Inserção em lista vs set
tempo_lista_ins = timeit.timeit(lambda: dados_lista.append(100_000), number=1000)
tempo_set_ins = timeit.timeit(lambda: dados_set.add(100_000), number=1000)
print(f"Inserção em lista: {tempo_lista_ins:.5f}s")
print(f"Inserção em set: {tempo_set_ins:.5f}s")
# Ambos são rápidos, mas set pode ser ligeiramente mais lento devido ao hash

Em geral, use sets e dicts para buscas rápidas e remoção de duplicatas; listas para sequências ordenadas e mutáveis; tuplas para dados imutáveis e leves. A escolha errada pode levar a código lento: por exemplo, usar uma lista para verificar pertinência em um grande conjunto de dados é ineficiente.

Boas práticas

  • Prefira tuplas a listas quando os dados não precisam ser modificados — isso torna o código mais seguro e pode melhorar a performance.
  • Use sets para remover duplicatas rapidamente: list(set(minha_lista)).
  • Ao iterar sobre um dict, lembre-se de que a ordem de inserção é preservada (Python 3.7+).
  • Evite usar listas como chaves de dicionário; use tuplas se precisar de uma chave composta.
  • Para grandes volumes de dados, considere a complexidade de memória: listas e tuplas são mais compactas que sets e dicts, que têm overhead de tabela hash.

Referências

Exercícios

  1. Explique por que usar uma lista para verificar se um elemento existe é menos eficiente do que usar um set, para grandes volumes de dados.

    ✓ Resposta: A lista realiza uma busca linear O(n) percorrendo todos os elementos, enquanto o set usa uma tabela hash que permite busca O(1) médio. Para grandes volumes, a diferença é drástica.
  2. Dado o seguinte cenário: você precisa armazenar as coordenadas (x, y) de pontos em um plano cartesiano, e esses pontos nunca serão modificados. Qual estrutura é mais adequada? Justifique.

    ✓ Resposta: Tupla. Como as coordenadas são imutáveis, a tupla é ideal: é hashable (pode ser usada como chave de dict, se necessário), mais leve que uma lista e protege contra alterações acidentais.
  3. Escreva um código que remova elementos duplicados de uma lista preservando a ordem original. Use uma estrutura adequada.

    ✓ Resposta:
    def remove_duplicatas_ordenado(lista):
        vistos = set()
        resultado = []
        for item in lista:
            if item not in vistos:
                vistos.add(item)
                resultado.append(item)
        return resultado
    
    # Exemplo:
    print(remove_duplicatas_ordenado([3, 1, 2, 1, 3, 4]))  # [3, 1, 2, 4]
    
  4. Qual é a complexidade de tempo para acessar um elemento pelo índice em uma lista e em uma tupla? E para acessar um valor por chave em um dict?

    ✓ Resposta: Tanto lista quanto tupla têm acesso O(1) por índice, pois são arrays contíguos. Dict tem acesso O(1) médio por chave devido à tabela hash.
  5. Crie um dicionário que mapeie nomes de alunos a suas notas. Em seguida, encontre o aluno com a maior nota. Use apenas estruturas nativas.

    ✓ Resposta:
    notas = {'Ana': 8.5, 'Bruno': 9.0, 'Carla': 7.5}
    melhor_aluno = max(notas, key=notas.get)
    print(melhor_aluno, notas[melhor_aluno])  # Bruno 9.0