2007 年 5 月 1 日 22 時 48 分

List#remove と index


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


次に注目するのは、削除ボタンである。

これらは、ルールの数に影響を与える処理なので、
実装において留意すべきことがある。

ルールは List<FilterRule> という集合型だ。
List から要素を削除する場合、remove メソッドを使うのだが、
remove には 2 種類のオーバーロードがある。

一つは int 型のインデックスを引数に取るもの。
もう一つは、削除するインスタンス自身を引数に取るもの。

今回の場合、要素が番号で受け渡されるため、
int 型を取るメソッドを使うのが自然に見えるが、
インデックスを扱う場合は注意が必要だ。

それは、リストの末尾以外に項目を追加・削除すると、
要素を表すインデックスがずれるということだ。

一度に複数の要素を削除する今回の場合、
単純にインデックスでループさせて #remove を呼ぶと、
予想もしない項目を削除してしまう危険性がある。

考えれば当り前のことなのだが、
コードを書く際には結構忘れがちになる。

以上を元に、2 種類の方法を考えてみよう。

1. インデックスを使って削除する場合

 削除する要素よりも前にある要素は、
 削除の前後でインデックスが変わらない。
 selected[] 配列を事前に降順にソートしておき、
 確実に大きいインデックスから削除していく。

2. インスタンスを使って削除する場合

 インデックスを元に削除する要素のインスタンスを取得し、
 それを別の List に集めてから一気に削除する。

1 は一般的に省メモリで高速だが、
配列に同じインデックスが複数ある場合に問題が起きるので、
Set 等を使って、事前に一意性を確保しなればならない。

2 は別の List を用意するという手間がかかるが、
一括削除には、removeAll という専用メソッドが使えるため、
コーディングの手間はそれほど悪くないし安全である。
ただ、規模が大きくなると速度に問題がある。

今回は規模が小さいので、2 の方を採用することにしよう。

========== ManageRulesAction#delete ==========
    public ActionForward delete(ActionMapping mapping,
            ActionForm form,
            HttpServletRequest request,
            HttpServletResponse response)
            throws Exception {

        // フォームを取得
        ManageFilterRulesForm manageForm =
                (ManageFilterRulesForm)form;

        // 選択されたルール番号を取得
        int[] selected = manageForm.getSelected();

        if (selected != null && selected.length > 0) {

            // 現在のフィルタ設定を読み込み
            Filter filter = FilterManager.load();

            // 削除するルールを集め
            List<FilterRule> rules = filter.getRules();
            List<FilterRule> targets =
                    new ArrayList<FilterRule>(
                            selected.length);
            for (int index : selected) {
                targets.add(rules.get(index));
            }

            // 一気に削除する
            rules.removeAll(targets);

            // 保存する
            FilterManager.save(filter);

        }

        // OK
        return mapping
                .findForward(Forwards.SUCCESS_KEY);

    }
========== end of ManageRulesAction#delete ==========

ちゃんとやるなら、IndexOutOfBoundsException や、
NullPointerException 例外を捕捉しておくべきだろうが、
今回、そのあたりは割愛することにする。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.