Swing/FastRemoveOfListItems のバックアップ(No.2)
- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- Swing/FastRemoveOfListItems へ行く。
- category: swing folder: FastRemoveOfListItems title: JListからの大量アイテム削除を高速化する tags: [JList, ListModel, AbstractListModel, DefaultListModel, SpringLayout] author: aterai pubdate: 2018-03-19T16:16:24+09:00 description: JListのListModelからの大量のアイテムを高速に削除する方法をテストします。 image: https://drive.google.com/uc?id=1w4uURJH6pPCGk68BT_XlCjxnZQlOxv1n7w
概要
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);
}
}
View in GitHub: Java, Kotlin解説
上記のサンプルでは、大量のアイテムをもつJList
を左右に配置し、選択アイテムを>
、<
ボタンで移動するテストを行っています。
default remove
- 移動元の
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
- 移動元の
JList
で選択されていないアイテムを別のArrayList
に保存 - 移動元の
JList
の選択インデックス配列をfor
ループで回して、移動元のDefaultListModel
へaddElement(...)
でコピー DefaultListModel#clear()
で移動元のJList
をクリア- 保存していた未選択アイテムリストから移動元の
JList
にアイテムを復元 AbstractListModel#fireIntervalRemoved(...)
はDefaultListModel#clear()
で一回呼ばれるだけなので高速
- 移動元の
addAll + remove
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%