惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
N
Netflix TechBlog - Medium
C
Check Point Blog
MyScale Blog
MyScale Blog
The GitHub Blog
The GitHub Blog
Blog — PlanetScale
Blog — PlanetScale
B
Blog RSS Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
WordPress大学
WordPress大学
博客园 - Franky
MongoDB | Blog
MongoDB | Blog
I
InfoQ
Hugging Face - Blog
Hugging Face - Blog
Recent Announcements
Recent Announcements
人人都是产品经理
人人都是产品经理
腾讯CDC
V
Visual Studio Blog
Engineering at Meta
Engineering at Meta
T
The Blog of Author Tim Ferriss
V
V2EX
云风的 BLOG
云风的 BLOG
Microsoft Azure Blog
Microsoft Azure Blog
U
Unit 42
B
Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Coleções Java
Matheus de G · 2026-05-09 · via DEV Community

O Java possui APIs úteis para o desenvolvimento e uma delas é a API Collections. A API de coleções do java é algo que você usará todo o dia, mesmo que não perceba.

Agrupar valores relacionados é algo comum no desenvolvimento, como notas de alunos, eventos que ocorreram em um certo dia ou uma lista de tarefas para serem feitas.

Esse agrupamento pode ser feitos de várias formas e o java collections fornece a API necessária para isso no java.

Array

A forma mais simples de agrupar valores é usando um Array. O array é uma estrutura de dados homogenia, ou seja, todos os valores possuem o mesmo tipo.

O Array deve ter tamanho fixo que pode ser declarado definindo os valores na inicialização ou definindo um tamanho usando new.

public class Main {
    public static void main(String[] args) {
        int[] notas = { 10, 8, 9, 7 };

        String[] tarefas = {
                "Dar like no post",
                "Comentar algo",
                "Compartilhar o post"
        };

        String[] diasDaSemana = new String[7];
        diasDaSemana[0] = "Domingo";
        diasDaSemana[1] = "Segunda-feira";
        diasDaSemana[2] = "Terça-feira";
        diasDaSemana[3] = "Quarta-feira";
        diasDaSemana[4] = "Quinta-feira";
        diasDaSemana[5] = "Sexta-feira";
        diasDaSemana[6] = "Sábado";
    }
}

Enter fullscreen mode Exit fullscreen mode

O array é alocado de forma contígua na memória. Isso significa que os espaços para cada dado no array é seguido um após o outro. O array notas estaria mais ou menos assim na memória.

Array na memória

A desvantagem é que caso não houver um o espaço contíguo na memória o array não conseguiria ser criado.

Memória ocupada

A vantagem disso é permitir um acesso instantâneo O(1) a qualquer valor do array. O programa faz uma conta simples para o acesso aos valores:

endereço = endereço_da_variavel_array + tamanho_do_tipo_de_dado * indice_do_array

Enter fullscreen mode Exit fullscreen mode

Por exemplo, vamos pegar o array notas, o tamanho de um int no java é 4 bytes, o índice de um array inicia em 0 e vamos imaginar que notas está no endereço 0x100 (256 em hexadecimal). Então a conta para acessar seria:

// acessando o índice 0 (primeiro índice)
x = 0x100 + 0x4 * 0x0
x = 0x100 + 0x0
x = 0x100 // 200 em hexadecimal que é o endereço onde está o primeiro valor

Enter fullscreen mode Exit fullscreen mode

E se quisermos o último valor? O último índice é 3 já que os índices vão de 0 até o tamanho do array - 1.

// acessando o índice 3 (último índice)
x = 0x100 + 0x4 * 0x3
x = 0x100 + 0xC
x = 0x10C // 268 em hexadecimal que é o endereço onde está o último valor

Enter fullscreen mode Exit fullscreen mode

Por isso o array é uma ótima estrutura para acessar valores.

Coleções

O array é a forma mais simples de agrupar valores. O java possui uma API completa para isso chamada Java Collections.

Essa API possui várias classes e interfaces para diferentes tipos de agrupamentos de valores e métodos para manipulação desses agrupamentos.

Abaixo temos a hierarquia das interfaces dessa API.

Hierarquia java collections

List

List é uma interface que defini operações para uma coleção de lista de valores, com ordem e índices. Temos duas principais implementações dessa interface, ArrayList e LinkedList.

ArrayList

O ArrayList uma implementação da interface List. Essa classe armazena os dados em um formato de array. O ArrayList pode aumentar ou diminuir o seu tamanho, diferente do array convencional que tem tamanho fixo.

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> linguagens = new ArrayList<>();

        linguagens.add("Java");
        linguagens.add("Python");
        linguagens.add("C++");

        linguagens.remove(1); // Remove "Python"

        linguagens.forEach(System.out::println); // imprimi os elementos
    }
}

Enter fullscreen mode Exit fullscreen mode

O ArrayList resolve o problema de tamanho fixo de um array. Internamente ele cria um array com um tamanho inicial de 10 por padrão.

A medida que você adiciona elementos com o método add() o ArrayList cria um novo array internamente e copia os dados para esse novo array.

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // internamente é um array de tamanho 10: Integer[] notas = new Integer[10];
        List<Integer> notas = new ArrayList<>();

        notas.add(10);
        notas.add(8);
        notas.add(9);
        notas.add(7);

        Integer nota = 6;
        notas.add(nota);

        // remoção por referência ao objeto
        notas.remove(nota); // Remove o objeto nota da lista
        System.out.println(notas); // [10, 8, 9, 7]

        // adiciona mais elementos no ArrayList
        // Internamente o ArrayList cria um novo array com mais capacidade e copia os elementos para ele
        for (int i = 0; i < 10; i++) {
            notas.add(i);
        }

        System.out.println(notas); 
        // [10, 8, 9, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    }
}

Enter fullscreen mode Exit fullscreen mode

LinkedList

O ArrayList resolve o problema de ter um array de tamanho fixo, mas ainda temos o problema de precisar ter espaço contíguo na memória para array, mesmo usando um ArrayList.

Para esse tipo de problema temos uma outra implementação da interface List chamada LinkedList. Essa classe é uma lista encadeada, onde cada valor é um nó da lista que aponta para o nó anterior e posterior. Se usarmos um LinkedList para armazenar aquele array de notas ficaria algo parecido com isso:

Lista encadeada na memória

A desvantagem desse tipo de implementação é que o acesso a um valor é linear O(n). Ou seja, se eu quiser o terceiro valor na lista, eu preciso acessar o primeiro e o segundo elemento até chegar no terceiro.

A vantagem desse tipo de implementação é que adicionar e remove valores é menos custoso já que é apenas necessário mudar o valor dos campos próximo e anterior dos nós o que é uma complexidade linear O(1).

Como LinkedList implementa a interface List podemos continuar com o código anterior e mudar apenas a classe ArrayList para LinkedList.

import java.util.LinkedList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> notas = new LinkedList<>(); // Alterando a implementação para LinkedList

        notas.add(10);
        notas.add(8);
        notas.add(9);
        notas.add(7);

        Integer nota = 6;
        notas.add(nota);

        // remoção por referência ao objeto
        notas.remove(nota); // Remove o objeto nota da lista
        System.out.println(notas); // [10, 8, 9, 7]

        for (int i = 0; i < 10; i++) {
            notas.add(i);
        }

        System.out.println(notas);
        // [10, 8, 9, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    }
}

Enter fullscreen mode Exit fullscreen mode

Set

O Set é outra interface que pertence ao java collections e representa uma estrutura de conjunto. Os conjuntos guardam elementos sem um índice ou ordem, como fazia com List, e não permite elementos duplicados.

Algumas implementações da interface Set são HashSet, TreeSet e LinkedHashSet.

Hierarquia Set

HashSet

O HashSet implementa a interface Set usando um tabela hash através da classe HashMap. Assim, quando você adiciona um valor ao HashSet ele cria um registro no HashMap interno com a chave sendo o valor que você adicionou e um Object para preencher o valor do HashMap

import java.util.HashSet;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Set<String> linguagens = new HashSet<>();

        String java = "Java";
        String javascript = "JavaScript";

        // internamente: HashMap: { "Java": Object }
        linguagens.add(java);

        // adicionar o mesmo valor faz apenas recriar a chave no HashMap
        linguagens.add(java);

        // internamente: HashMap: { "Java": Object, "JavaScript": Object }
        linguagens.add(javascript);

        /*
         * internamente:
         * HashMap: {
         * "Java": Object,
         * "C": Object,
         * "Typescript": Object,
         * "JavaScript": Object
         * }
         */
        linguagens.add("Typescript");
        linguagens.add("C");

        System.out.println(linguagens); // [Java, C, Typescript, JavaScript]

        linguagens.remove(javascript); // Remove "JavaScript" do conjunto

        System.out.println(linguagens); // [Java, C, Typescript]
    }
}

Enter fullscreen mode Exit fullscreen mode

Esse tipo de implementação permite um acesso constante O(1) já que acessar um valor na tabela hash é acesso constante.

Note que mesmo adicionando a string "C" por último, ela foi impressa como sendo a segunda. Isso, porque o Set não garante uma ordem dos elementos.

LinkedSet

Essa implementação do Set utiliza uma lista duplamente encadeada para manter a ordem dos elementos e um HashMap para implementar os métodos da interface Set.

import java.util.LinkedHashSet;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Set<String> linguagens = new LinkedHashSet<>(); // Trocando para LinkedHashSet

        String java = "Java";
        String javascript = "JavaScript";

        /*
         * internamente:
         * HashMap: { "Java": Object }
         * LinkedList: head <-> "Java" <-> tail
         */
        linguagens.add(java);

        // adicionar o mesmo valor faz apenas recriar a chave no HashMap
        linguagens.add(java);

        /*
         * internamente:
         * HashMap: { "Java": Object, "JavaScript": Object }
         * LinkedList: head <-> "Java" <-> "JavaScript" <-> tail
         */
        linguagens.add(javascript);

        /*
         * internamente:
         * HashMap: {
         * "Java": Object,
         * "JavaScript": Object,
         * "Typescript": Object,
         * "C": Object
         * }
         * LinkedList: head <-> "Java" <-> "JavaScript" <-> "Typescript" <-> "C" <-> tail
         */
        linguagens.add("Typescript");
        linguagens.add("C");

        System.out.println(linguagens); // [Java, JavaScript, Typescript, C]

        linguagens.remove(javascript); // Remove "JavaScript" do conjunto

        System.out.println(linguagens); // [Java, Typescript, C]
    }
}

Enter fullscreen mode Exit fullscreen mode

TreeSet

O TreeSet é um implementação da interface NavigableSet que extende o SortedSet que é um interface que extende o Set. O SortedSet são conjuntos ordenados. Internamente o TreeSet usa uma Árvore Rubro-Negra que é uma árvore binária de busca auto balanceada, um tipo de estrutura de dados, para guarda os valores ordenados.

O TreeSet ordena os valores que podem ser comparados, ou seja, que implementam a interface Comparable. Caso o valor não implemente a interface Comparable, pode ser passado um Comparator no construtor do TreeSet para que a classe saiba como ordenar os valores.

A impressão dos valores utiliza o algoritmo de percurso "em ordem" das árvores binárias.

import java.util.Set;
import java.util.TreeSet;

public class Main {
    static record Product(String name, double price) {
    }

    static class ProductCompable implements Comparable<ProductCompable> {
        String name;
        double price;

        public ProductCompable(String name, double price) {
            this.name = name;
            this.price = price;
        }

        @Override
        public int compareTo(ProductCompable other) {
            return Double.compare(this.price, other.price);
        }
    }

    public static void main(String[] args) {
        // Passando um Comparable
        Set<ProductCompable> productSetComparable = new TreeSet<>();

        // Passando um Comparator através de uma expressão lambda para ordenar
        Set<Product> productSet = new TreeSet<>((p1, p2) -> Double.compare(p1.price, p2.price));

        productSet.add(new Product("Laptop", 999.99));
        productSet.add(new Product("Smartphone", 499.99));
        productSet.add(new Product("Tablet", 299.99));
        productSet.add(new Product("Headphones", 199.99));
        productSet.add(new Product("Smartwatch", 199.50));
        productSet.add(new Product("Camera", 599.99));

        // Imprime os produtos usando o percurso "em ordem" de árvores binárias de busca
        productSet.forEach(System.out::println);
        /* 
         Product[name=Smartwatch, price=199.5]
         Product[name=Headphones, price=199.99]
         Product[name=Tablet, price=299.99]
         Product[name=Smartphone, price=499.99]
         Product[name=Camera, price=599.99]
         Product[name=Laptop, price=999.99]
        */
    }
}

Enter fullscreen mode Exit fullscreen mode

A árvore gerada pelo código acima seria algo assim:

Árvore Rubro-Negra

Queue e Deque

Outra interface que pertence ao Java Collection é a Queue. Essa interface representa uma estrutura de fila.

Filas tem a propriedade de FIFO (Fisrt In Last Out), o primeiro elemento a entrar na fila será sempre o primeiro a sair dela.

Fila

A Deque é um tipo especialmente de fila, uma "fila de duas pontas". Ela permite inserir e excluir da fila pelo inicio (head) ou final (tail).

Deque adiciona métodos com sufixo Fist e Last para manipular a fila como addFirst() e addLast.

Algumas classes que implementam a interface Queue e Deque são LinkedList, PriorityQueue e ArrayDeque.

Hierarquia Queue

PriorityQueue

A PriorityQueue é uma fila de prioridade. Ela usa um array para armazenar os valores, mas ela também usa a interface Comparable para definir a prioridade dos elementos adicionados.

Caso a classe que a PriorityQueue guarda não implemente Comparable, pode ser passado um Comparator no construtor para definir o critério de prioridade que será usado.

import java.util.PriorityQueue;
import java.util.Queue;

public class Main {
    record Person(String name, int age) {
    }

    public static void main(String[] args) {
        Person jose = new Person("José", 30);
        Person zezinho = new Person("Zezinho", 25);
        Person seuZe = new Person("Seu Zé", 65);
        Person maria = new Person("Maria", 70);

        // Prioridade de atendimento: pessoas mais velhas têm prioridade
        Queue<Person> atendimento = new PriorityQueue<>((p1, p2) -> Integer.compare(p2.age(), p1.age()));

        // Adicionando a fila. Sempre no final da fila
        atendimento.add(jose);
        atendimento.add(maria);
        atendimento.add(zezinho);
        atendimento.add(seuZe);

        System.out.println("Fila de atendimento: " + atendimento.stream().map(Person::name).toList());
        // Fila de atendimento: [Maria, Seu Zé, Zezinho, José]

        // Removendo o primeiro da fila. Sempre o primeiro da fila
        Person atendido = atendimento.poll();
        System.out.println("Atendido: " + atendido.name()); // Atendido: Maria

        System.out.println("Fila após o atendimento: " + atendimento.stream().map(Person::name).toList());
        // Fila após o atendimento: [Seu Zé, Zezinho, José]

        // Verificando o primeiro da fila sem removê-lo
        System.out.println("Primeiro da fila: " + atendimento.peek().name()); // Primeiro da fila: Seu Zé
    }
}

Enter fullscreen mode Exit fullscreen mode

LinkedList

Além de implementar a interface List, o LinkedList implementa a interface Deque. Como uma lista encadeada tem um referência para o inicio (head) e para o final (tail) da lista, para funcionar como uma fila dupla é só usar essas referências para inserir e remover no final e remover no inicio da lista encadeada.

Lista encadeada

import java.util.Deque;
import java.util.LinkedList;

public class Main {
    record Person(String name, int age) {
    }

    public static void main(String[] args) {
        Person jose = new Person("José", 30);
        Person zezinho = new Person("Zezinho", 25);
        Person seuZe = new Person("Seu Zé", 65);
        Person maria = new Person("Maria", 70);

        Deque<Person> atendimento = new LinkedList<>();

        // Adicionando no final da fila
        atendimento.add(jose);
        atendimento.add(zezinho);

        // Adicionando no início da fila
        atendimento.addFirst(seuZe);
        atendimento.addFirst(maria);

        System.out.println("Fila de atendimento: " + atendimento.stream().map(Person::name).toList());
        // Fila de atendimento: [Maria, Seu Zé, José, Zezinho]

        // Removendo o primeiro da fila
        Person atendido = atendimento.poll();
        System.out.println("Atendido: " + atendido.name());
        // Atendido: Maria

        // Removendo o último da fila
        Person person = atendimento.pollLast();
        System.out.println("Desistiu da fila: " + person.name());
        // Desistiu da fila: Zezinho

        System.out.println("Fila de atendimento: " + atendimento.stream().map(Person::name).toList());
        // Fila de atendimento: [Seu Zé, José]

        // Verificando o primeiro da fila sem removê-lo
        System.out.println("Primeiro da fila: " + atendimento.peek().name());
        // Primeiro da fila: Seu Zé
    }
}

Enter fullscreen mode Exit fullscreen mode

ArrayDeque

O ArrayDeque é uma classe que implementa o Deque. Usa internamente um array circular de tamanho 16 para armazenar os valores.

Internamente, o ArrayDeque guarda o índice do inicio (head) e do fim (tail) do array circular. A medida que adicionar valores no fim com o método add(), por exemplo, a classe altera o valor do tail. E quando adicionar no inicio com o método addFist(), por exemplo, a classe altera o head.

Array circular

Quando não há mais espaço no array, um novo array com mais capacidade é criado e os dados são copiados para esse novo array

import java.util.ArrayDeque;
import java.util.Deque;

public class Main {
    public static void main(String[] args) {
        Deque<Integer> deque = new ArrayDeque<>();

        // adiciona no fim e altera o valor de tail
        deque.add(3);
        deque.add(22);
        deque.addLast(10);

        // adiciona no início e altera o valor de head
        deque.addFirst(15);

        System.out.println(deque);
        // [15, 3, 22, 10]

        // Cria um novo array com mais capacidade e copia os elementos do array antigo
        // para o novo array
        for (int i = 0; i < 100; i++) {
            deque.add(i);
        }

    }
}

Enter fullscreen mode Exit fullscreen mode

Iterable e Collection

Subindo ao nível mais genérico, temos as interfaces Collection e Iterable.

O Collection é uma coleção genérica. Todas as classes do Java Collection implementam essa interface. Se, por exemplo, você precisa modificar qualquer tipo de coleção do Java Collection através de um método, você pode usar um Collection como argumento.

O Iterable é um coleção objetos que pode ser ser lida e ser percorrida um objecto por vez. Se, por exemplo, você precisa acessar cada valor de uma coleção de objetos usando um método você pode receber como argumento um Iterable. Cada classe tem uma forma de guardar os valores e por isso cada uma tem sua implementação de Iterable para pode ser navegável de acordo com sua implementação usando um Iterator, um objeto que sabe navegar por um Iterable.

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        Queue<Integer> queue = new ArrayDeque<>();
        Deque<Integer> deque = new LinkedList<>();
        Set<Integer> set = new HashSet<>();

        modifyCollection(list);
        modifyCollection(queue);
        modifyCollection(deque);
        modifyCollection(set);

        System.out.print("List:");
        printCollection(list); // List: 42

        System.out.print("Queue:");
        printCollection(queue); // Queue: 42

        System.out.print("Deque:");
        printCollection(deque); // Deque: 42

        System.out.print("Set:");
        printCollection(set); // Set: 42
    }

    static void printCollection(Iterable<Integer> iterable) {
        // Iterables podem ser usados nesse tipo de for
        for (Integer element : iterable) {
            System.out.println(element);
        }
    }

    static void modifyCollection(Collection<Integer> collection) {
        if (collection.isEmpty()) {
            collection.add(42);
        } else {
            collection.remove(collection.iterator().next());
        }
    }
}

Enter fullscreen mode Exit fullscreen mode