JListからの大量アイテム削除を高速化する
Total: 3745, Today: 1, Yesterday: 0
Posted by aterai at
Last-modified:
Summary
JListのListModelからの大量のアイテムを高速に削除する方法をテストします。
Screenshot

Advertisement
Source Code Examples
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, KotlinDescription
上記のサンプルでは、大量のアイテムをもつ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のアイテム削除には無関係