2

This is an example where you press a button and jList1 is refilled with items from a1 to a1000.:

//variable
private List<String> list = new ArrayList<>();

...

//main method
jList1.setModel(new DefaultListModel());
for(int i = 0; i < 1000; i++) {
    list.add("a"+i);
}

...

//button action - jList1 refill
DefaultListModel dtm = (DefaultListModel)jList1.getModel();
dtm.removeAllElements();
for(String s : list) {
    dtm.addElement(s);
}

If I fill the jList1, then select (with mouse) 0 index (first element in jList) and then press the button the program freezes while refilling the list. If I select any other element or do not select any item in the list at all then it fills just fine.

P.S. This example is done without any swing or EWT threads, because the main reason was found using them.

SSCCE:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package lt;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;

/**
 *
 * @author Minutis
 */
public class Window {

    public static void main(String[] args) {
        final List<String> list = new ArrayList<>();


        JFrame frame = new JFrame("BorderLayout Frame");
        JPanel panel = new JPanel();
        final JList jList1 = new JList();
        JButton refill = new JButton("Refill");

        jList1.setModel(new DefaultListModel());
        for(int i = 0; i < 1000; i++) {
            list.add("a"+i);
        }

        refill.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                DefaultListModel dtm = (DefaultListModel)jList1.getModel();
                dtm.removeAllElements();
                for(String s : list) {
                    dtm.addElement(s);
                }
            }

        });

        frame.add(panel);
        panel.setLayout(new BorderLayout());
        panel.add(jList1, BorderLayout.CENTER);
        panel.add(refill, BorderLayout.SOUTH);
        frame.setSize(300, 300);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}
Minutis
  • 1,193
  • 1
  • 17
  • 47
  • Do you get an Exception? – Kai Nov 21 '12 at 13:34
  • No, no exceptions this is why I write here. – Minutis Nov 21 '12 at 13:35
  • Can you post a [SSCCE](http://sscce.org/)? – Jakub Zaverka Nov 21 '12 at 13:40
  • works for me in similair code, nobody knows how is your rest, maybe important then post an SSCCE generated a.m. issue, otherwise have to waiting for miracle – mKorbel Nov 21 '12 at 13:47
  • Do you really understand the problem? It works, but it freezes for a couple of seconds (4-5) when you select first list element and click the button. The more elements are added the more that freeze is visible. – Minutis Nov 21 '12 at 14:02
  • It is not freezing, it is lagging. Now I see, maybe it's a solution to remove the selection and set it after the refresh. – Kai Nov 21 '12 at 14:05
  • Selection removing does not help. And I am afraid that I do not know how to use debugging. – Minutis Nov 21 '12 at 14:10
  • Something weird I tried: change the removeAll for while(!model.isEmpty()) model.removeElementAt(0); Now, no matter what index you pick, it is slow but super fast if o index selected...there is something going on in the background of the model... – doctor killer Nov 21 '12 at 14:24
  • @doctorkiller I tried your exmample, it's not super fast (removeAllElements while selected nothing works faster) but it's much better than the freeze case. Anyone has any thoughts about this? – Minutis Nov 21 '12 at 14:38
  • 1
    @Minutis Yes I know: this was to illustrate that the removeAllElements seems to do something different when there is an index selected...anyway a good workaround it to do jList1.setSelectedIndex(1); before you empty the list: I looked at the java source code and it checks if the index so your code would never crash...not an acceptable answer but a workaround...very interesting "bug" if there is anything to be done to fix this, it would be in the JList and not in the model... – doctor killer Nov 21 '12 at 14:46

2 Answers2

5

The problem is cause by removing all elements in the model when the first element is selected. The way it is implemented, when an index is selected and is removed, for some reason it generates a selectionChanged event. If another index is selected while another is deleted (even if it meant element shifting), it doesn't generate a selectionChanged event.

The removal is implemented in a way that it periodically removes the first element, while the selection index remains unchanged. So for the case of index 0 being selected, it generates 1000 selectionChanged events, which must be handled by the EDT and listeners. If you select another index, only 1 event is generated. That is significantly less overhead.

Before the repopulating while index 0 is selected, try manually setting the selection to index 1:

if(jList1.getSelectedIndex() == 0){
     jList1.setSelectedIndex(1);
}

I think there is no need to worry if there is only one element in the list - it shouldn't do anything to set index greater that element count. But that may be implementation-specific.

For some reason, clearSelection() will cause selectionChanged events to be generated for repopulating an empty list.

Jakub Zaverka
  • 8,816
  • 3
  • 32
  • 48
  • see my edit here +1 for another choice, but I'm doubt in, JList caused some of performance issue ... – mKorbel Nov 21 '12 at 14:47
  • Thanks this really works, if I put these three lines before `jList1.removeAllElements();` – Minutis Nov 21 '12 at 14:53
  • no idea, can you take my SSCCE and modify to your view, maybe orders of code line depends in this case :-), btw clearSelection and delay by Swing Timer (or trhead.sleep == don't talk about) solved this issue – mKorbel Nov 21 '12 at 14:53
  • @Minutis not freeeze as you described, have to waiting for Jakub :-) – mKorbel Nov 21 '12 at 14:55
2
  • logical & better could (just to avoiding mistakes from typos) be to add / remove / modify Items directly in XxxListModel,

for example

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Testing extends JFrame {

    private static final long serialVersionUID = 1L;
    private DefaultListModel listModel = new DefaultListModel();
    private JList list = new JList(listModel);
    private int currentSelectedRow = 0;
    private int xX = 0;

    public Testing() {
        setLocation(400, 300);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        for (int x = 0; x < 51; x++) {
            listModel.addElement("" + x);
            xX++;
        }
        JScrollPane sp = new JScrollPane(list);
        final JViewport vp = sp.getViewport();
        add(sp, BorderLayout.CENTER);
        /*JButton btn = new JButton("Get Row:");
        add(btn, BorderLayout.SOUTH);
        btn.addActionListener(new ActionListener() {

        public void actionPerformed(ActionEvent ae) {
        try {
        int goToRow = Integer.parseInt(JOptionPane.showInputDialog(getContentPane(), "Go To Row:"));
        Rectangle r = list.getCellBounds(goToRow, goToRow);
        if (goToRow > currentSelectedRow) {
        r = list.getCellBounds(goToRow - 1, goToRow - 1);
        int vph = vp.getExtentSize().height;
        r.y += vph;
        }
        list.scrollRectToVisible(r);
        list.setSelectedIndex(goToRow);
        currentSelectedRow = goToRow;
        } catch (Exception e) {

        }
        }
        });*/
        JButton btn1 = new JButton("Reset Model CastingModel");
        add(btn1, BorderLayout.NORTH);
        btn1.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent ae) {
                DefaultListModel model = (DefaultListModel) list.getModel();
                model.removeAllElements();
                for (int x = 0; x < 51; x++) {
                    model.addElement("" + (x + xX));
                    xX++;
                }
                //list.setModel(model);
            }
        });

        JButton btn2 = new JButton("Reset Model directly from Model");
        add(btn2, BorderLayout.SOUTH);
        btn2.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent ae) {
                listModel.removeAllElements();
                for (int x = 0; x < 51; x++) {
                    listModel.addElement("" + (x + xX));
                    xX++;
                }
            }
        });

        pack();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Testing().setVisible(true);
            }
        });
    }
}

EDIT list.clearSelection(); hasn't any issue, everything moreover than 1k rows caused with desribed issue by OPs

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Testing extends JFrame {

    private static final long serialVersionUID = 1L;
    private DefaultListModel listModel = new DefaultListModel();
    private JList list = new JList(listModel);
    private int currentSelectedRow = 0;
    private int xX = 0;

    public Testing() {
        setLocation(400, 300);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        for (int x = 0; x < 999; x++) {
            listModel.addElement("" + x);
            xX++;
        }
        JScrollPane sp = new JScrollPane(list);
        final JViewport vp = sp.getViewport();
        add(sp, BorderLayout.CENTER);

        JButton btn1 = new JButton("Reset Model CastingModel");
        add(btn1, BorderLayout.NORTH);
        btn1.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent ae) {
                list.clearSelection();
                DefaultListModel model = (DefaultListModel) list.getModel();
                model.removeAllElements();
                for (int x = 0; x < 999; x++) {
                    model.addElement("" + (x + xX));
                    xX++;
                }
                //list.setModel(model);
            }
        });

        JButton btn2 = new JButton("Reset Model directly from Model");
        add(btn2, BorderLayout.SOUTH);
        btn2.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent ae) {
                list.clearSelection();
                listModel.removeAllElements();
                for (int x = 0; x < 999; x++) {
                    listModel.addElement("" + (x + xX));
                    xX++;
                }
            }
        });

        pack();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Testing().setVisible(true);
            }
        });
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Still the same problem, you won't feel the freeze with your element count - increase it from 50 to 5000. Then do not select anything in list and press any of those two buttons. There will be NO lag. But if you first select FIRST element of the list then press any of those two buttons - you will see THE LAG. – Minutis Nov 21 '12 at 14:30
  • right, (I never put huge amout of Items to the JList) please is there limit about Items on your side, under <1k this notifiers works correctly implemeneted in API, for JComboBox, JList and JTable too for Java6/7 on WinXp, see my edit, everything moreover caused a.m. described issue, – mKorbel Nov 21 '12 at 14:41