Como inserir itens em JTable utilizando um TableModel?

2014/12/12

Primeiramente quero que você memorize o seguinte:

Se você memorizou isso, podemos continuar...

Frequentemente vejo pessoas perguntando "como adiciono itens em uma JTable?" e para minha surpresa, estou exausto de ver outras pessoas ensinado a utilizar DefaultTableModel, fazendo um laço desnecessários para inserir itens nesse DefaultTableModel através do método "addRow".

Pois bem, coloque uma coisa na cabeça: O Swing Framework é completamente MVC, às vezes acho que é MVC até demais, com isso, você não precisa por exemplo fazer laços desnecessários para inserir itens em uma JTable, ao invés disso, você cria uma CLASSE MODELO que irá fornecer as informações que seu componente deve renderizar. Isso se aplica não somente à JTable, mas a todos os outros componentes do Swing Framework.

Imagine por exemplo que você iterou um ResultSet e preencheu uma List com objetos que você criou a partir da consulta ao banco de dados. Você já fez um laço iterando o ResultSet, não precisa fazer outro laço para inserir a representação desses objetos numa JTable, ao invés disso, crie sua própria implementação de TableModel para renderizar essa List em uma JTable.

Logo abaixo apresento um exemplo hipotético com valores fixos. Esse exemplo serve para mostrar que você pode fazer uma JTable renderizar 10 mil itens sem você ter feito nenhum laço para inserir itens nela.

Observe os comentários no código, eles lhe ajudarão a entender as modificações necessárias para renderizar qualquer outro objeto.

import java.awt.*;

import javax.swing.*;
import javax.swing.table.*;

@SuppressWarnings("serial")
public class ExemploTableModel extends JFrame {

    /**
     * Modelo hipotético para fornecer dados para a JTable<BR>
     */
    private class MeuModeloComMuitasLinhas extends AbstractTableModel {

        /**
         * A JTable chama esse método para saber quantas colunas ela possui
         */
        @Override
        public int getColumnCount() {
            // minha JTable terá 3 colunas
            // poderia ter tantas quanto eu quisesse
            return 3;
        }

        /**
         * A JTable chama esse método para saber qual é o nome da coluna informada
         */
        @Override
        public String getColumnName(int col) {
            // o nome das colunas será "coluna 0", "coluna 1" e "coluna 2"
            // mas poderia ser algo mais óbvio como "Nome", "Idade" e "Gênero" por exemplo
            return "Coluna " + col;
        }

        /**
         * A JTable chama esse método para saber quantos itens ela possui
         */
        @Override
        public int getRowCount() {
            // minha JTable terá 10 mil linhas
            // mas poderia por exemplo retornar o tamanho da lista de objetos que quero renderizar
            return 10000;
        }

        /**
         * A JTable chama esse método para saber qual o valor que ela deve apresentar na célula informada
         */
        @Override
        public Object getValueAt(int lin, int col) {
            // cada célula da minha JTable vai renderizar sua linha e sua coluna
            // mas poderia fazer o seguinte:
            // pegar o objeto na posição "lin" da lista
            // um switch da coluna e com isso
            // devolver o valor dos atributos "nome", "idade" e "genero"
            return "célula (" + lin + ", " + col + ")";
        }
    }

    /**
     * Ponto de entrada do exemplo
     */
    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            JFrame janela = new ExemploTableModel();
            janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            janela.setSize(640, 480);
            janela.setLocationRelativeTo(null);
            janela.setVisible(true);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    private ExemploTableModel() {
        super("Exemplo");

        /*
         * Vejam, aqui eu digo para a JTable utilizar o meu modelo de dados assim, não preciso me preocupar em fazer laços para inserir itens
         */
        JTable table = new JTable(new MeuModeloComMuitasLinhas());

        JScrollPane scroll = new JScrollPane(table);
        Container panel = getContentPane();
        panel.add(BorderLayout.CENTER, scroll);
    }
}

Perceba como fica um código limpo e objetivo, sem contar que um TableModel implementado dessa forma é bem mais performático do que ficar removendo itens da JTable e em seguida fazendo laços para inserir conteúdo atualizado. Dessa forma, basta você atualizar o estado dos objetos em sua List que para a JTable a alteração será transparente.