Las listas de datos son elementos muy similares a aquellos que están compuestos por parte gráfica y parte de datos. Para poder construirla se utiliza:

JList lista = new JList(opciones);

En este constructor también se puede identificar el modelo de datos. Los métodos más utilizados para el elemento son:

//Número de filas que tendrá el elemento
lista.setVisibleRowCount(-1);
//Modo de mostrar el elemento HORIZONTAL_WRAP / VERTICAL_WRAP / VERTIAL
lista.setLayoutOrientation(JList.HORIZONTAL_WRAP);
//Modo de seleccion que tendrá el elemento SINGLE_SELECTION / SINGLE_INTERVAL_SELECTION / MULTIPLE_INTERVAL_SELECTION
lista.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

Manejo de Eventos

Para el manejo de eventos se puede realizar desde el modelo de selección o bien desde el elemento gráfico directamente

modeloSeleccion.addListSelectionListener(new ListSelectionListener() {
    @Override
    public void valueChanged(ListSelectionEvent e) {
        System.out.println(lista.getSelectedValue());
    }
});

lista.addListSelectionListener(new ListSelectionListener() {
    @Override
    public void valueChanged(ListSelectionEvent e) {
        System.out.println(lista.getSelectedValue());
    }
});

Modelo de datos

Para poder manejar los datos existen dos modelos. El primer modelo se trata del que maneja la selección de la lista, mientras que el otro maneja los datos en su totalidad.

ListSelectionModel modeloSeleccion = lista.getSelectionModel();
DefaultListModel modeloLista = new DefaultListModel();
modeloLista.addElement("Opcion 1");
modeloLista.remove(0);
modeloLista.getElementAt(0);
lista.setModel(modeloLista);

Del mismo modo se puede crear un modelo propio implementando de ListModel

public class ModeloLista implements ListModel {
    @Override
    public int getSize() {
        return 0;
    }

    @Override
    public Object getElementAt(int index) {
        return null;
    }

    @Override
    public void addListDataListener(ListDataListener l) {

    }

    @Override
    public void removeListDataListener(ListDataListener l) {

    }
}

En el caso de querer manejar datos de una lista, se tendría que crear un constructor con los datos asociados.