2007 年 5 月 2 日 23 時 23 分

複数同時移動の処理


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


次は上へ・下へボタンの処理だ。

これもインデックスを使っての操作となるが、
削除のように簡単にはいかない。
ちょっとしたアルゴリズムとも言えるかも。

これはインデックスを使って入れ替えが必要なので、
昨日と同じように、インデックスのずれにも注意が必要だ。

上に動かす場合を例として、方針を練ってみよう。

基本は、選択したものをそのまま 1 つ上にずらすのだが、
飛び飛びに選択した状態で上に移動したとしても、
飛び飛びの状態をそのまま保つように移動させることにする。

例えば、A B C D E というルールがあり、
B と D を上に移動すると、B A D C E となる。
この状態でさらに B と D を選択して上に移動しても、
B と D の位置関係を保つため、変化させない。
つまり、D と A は入れ替えないことにする。

こういう風に設計しておけば、以下のような仮定ができる。

    1. チェックされたルールはすべて移動するか、
       すべて移動しないかのどちらかである。
    2. 最も下が選択された場合、下への移動はできない。
       最も上が選択された場合、上への移動はできない。

こうすると実装に関して少し楽ができるのだ。
移動可能か不可能かは、端の要素が選択されてるかで判断し、
可能であれば、すべてのインデックスに対して、
その上(または下)の要素と入れ替えるという処理となる。

この入れ替えは、削除の時と同じく、順序が大事だ。
上に移動する場合は、上の要素から順番に入れ替えないと、
誤って選択された要素同士を入れ替える危険性がある。
⇒これは、昇順に並べ替えることで対処しよう。

また、インデックスで処理を行う場合、
同じインデックスが複数含まれると困ったことになる。
そのため、選択された番号が一意であるか調べる必要がある。
これは、律儀に調べてもいいが、Set を使う手もある。
Set インタフェースは、要素の重複を許さないからだ。

では、この方針に従って、上への移動を実装してみよう。

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

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

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

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

            // 重複を省き、かつ昇順に並べる
            SortedSet<Integer> indexes =
                    new TreeSet<Integer>();
            for (int index : selected) {
                indexes.add(index); // ボックス化
            }

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

            List<FilterRule> rules = filter.getRules();

            // 最初のルールが選択されていたら動かせない
            if (indexes.first() > 0) {

                // 前から順に上と交換
                for (int index : indexes) {
                    FilterRule rule = rules.get(index);
                    rules.set(index, rules
                            .get(index - 1));
                    rules.set(index - 1, rule);
                }

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

            }

        }

        // OK
        return mapping
                .findForward(Forwards.SUCCESS_KEY);
    }
========== end of ManageRulesAction#moveUp ==========

Set<T> を使うには、要素をオブジェクトにする必要がある。
なので、int である要素番号は Integer にボックス化する。
Java では、配列を一気にボックス化できないので、
地道に各要素を列挙して格納する。

どうせ並び替えをしなければならないのであれば、
順番の規定も行う SortedSet 実装の TreeSet を使おう。

最後に、SortedSet を拡張 for 構文で列挙。
TreeSet の保障により、確実に一意の値が昇順に返される。
あとはこのインデックスを使って要素の入れ替えを行う。

しかし処理速度は遅そうだ。コードが読みやすいので良いけど。



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