JListからの大量アイテム削除を高速化する
Total: 3308
, Today: 2
, Yesterday: 1
Posted by aterai at
Last-modified:
概要
JList
のListModel
からの大量のアイテムを高速に削除する方法をテストします。
Screenshot
Advertisement
サンプルコード
private static <E> void move1(JList<E> from, JList<E> to) {
ListSelectionModel sm = from.getSelectionModel();
int[] selectedIndices = from.getSelectedIndices();
DefaultListModel<E> fromModel = (DefaultListModel<E>) from.getModel();
DefaultListModel<E> toModel = (DefaultListModel<E>) to.getModel();
List<E> unselectedValues = new ArrayList<>();
for (int i = 0; i < fromModel.getSize(); i++) {
if (!sm.isSelectedIndex(i)) {
unselectedValues.add(fromModel.getElementAt(i));
}
}
if (selectedIndices.length > 0) {
for (int i: selectedIndices) {
toModel.addElement(fromModel.get(i));
}
fromModel.clear();
// unselectedValues.forEach(fromModel::addElement);
DefaultListModel<E> model = new DefaultListModel<>();
unselectedValues.forEach(model::addElement);
from.setModel(model);
}
}
View in GitHub: Java, Kotlin解説
上記のサンプルでは、大量のアイテムをもつJList
を左右に配置し、選択アイテムを「>
」と「<
」ボタンで移動するテストを行っています。
default remove
(5000
件)- 移動元の
JList
で選択されているアイテムのインデックスをJList#getSelectedIndices()
メソッドで取得 - このインデックス配列を
for
文でループして移動元のDefaultListModel
からget(idx)
でアイテムを取得し、これを移動先のDefaultListModel
へaddElement(...)
メソッドでコピー - このインデックス配列を
for
文で末尾からループし、移動元のDefaultListModel
からremove(idx)
でアイテムを削除 DefaultListModel#remove(idx)
内で実行されるAbstractListModel#fireIntervalRemoved(...)
が遅いため、アイテムを大量に削除する場合非常に時間が掛かるDefaultListModel#removeAllElements()
で全削除、JList
の選択モードがListSelectionModel.MULTIPLE_INTERVAL_SELECTION
でDefaultListModel#removeRange(...)
メソッドで範囲削除が可能な場合、AbstractListModel#fireIntervalRemoved(...)
は最後に一回呼ばれるだけなので高速
- 移動元の
clear + addElement
(20000
件)- 移動元の
JList
で選択されていないアイテムを別のArrayList
に保存 - 移動元の
JList
の選択インデックス配列をfor
ループで回して移動元のDefaultListModel
へaddElement(...)
でコピー DefaultListModel#clear()
で移動元のJList
をクリア- 保存していた未選択アイテムリストから移動元の
JList
にアイテムを復元 AbstractListModel#fireIntervalRemoved(...)
はDefaultListModel#clear()
で一回呼ばれるだけなので高速- リストの先頭の
1
件を選択して移動すると時間がかかる- 先頭を含まない場合は高速
DefaultListModel
を新規生成して入れ替えれば高速
- 移動元の
addAll + remove
(20000
件)AbstractListModel
を継承するリストモデルを作成DefaultListModel
で使用しているVector
ではなくArrayList
をアイテムの保持に使用- 選択アイテムのインデックス配列を引数にしてまとめて削除を実行するメソッドを追加
AbstractListModel#fireIntervalRemoved(...)
は最後に一回呼ばれるだけなので高速public void remove(int... selectedIndices) { if (selectedIndices.length > 0) { int max = selectedIndices.length - 1; for (int i = max; i >= 0; i--) { delegate.remove(selectedIndices[i]); } fireIntervalRemoved(this, selectedIndices[0], selectedIndices[max]); } }
JTable
でも大量の行を高速に削除する場合は、同様の方法を取る必要があるList<Vector> unselectedRows = new ArrayList<>(model.getRowCount()); ListSelectionModel sm = table.getSelectionModel(); for (int i = 0; i < model.getRowCount(); i++) { if (!sm.isSelectedIndex(i)) { int idx = table.convertRowIndexToModel(i); unselectedRows.add((Vector) model.getDataVector().get(idx)); } } model.setRowCount(0); unselectedRows.forEach(model::addRow);
- 左右の
JList
、中央のJButton
の配置は、SpringLayout
を使用してレイアウト- SpringLayoutの使用
- 左右の
JList
: 親パネルの幅の40%
- 中央の
JButton
を縦に配置したBox.createVerticalBox()
: 親パネルの幅の10%
- 中央の幅を固定にする場合は、Componentの3列配置、中央幅固定、左右均等引き伸ばしを行うLayoutManagerを作成するのような方法がある
- JList#setValueIsAdjusting(boolean)は
ListSelectionModel
用なのでListModel
のアイテム削除には無関係