このアーカイブは同期化されません。 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 例外を捕捉しておくべきだろうが、
今回、そのあたりは割愛することにする。