基本情報技術者 2016年 春期 午後 問11
次のJavaプログラムの説明及びプログラムを読んで、設問1,2に答えよ。
〔プログラムの説明〕
“すべきこと”(以下、ToDoという)を管理するプログラムである。
(1) クラスToDoはToDoを表すクラスであり、コンストラクタで主題、期限、重要度を指定する。期限は、年月日を表す8桁又は年月日時分を表す12桁の数字から成る文字列(以下、日時という)であり、例えば、2016年4月16日を表す文字列は“20160416”、2016年4月16日午後1時0分を表す文字列は“201604161300”である。ここで、日時に誤りはないものとする。
主題、期限、重要度を取得する各メソッドと、状態を設定及び取得するメソッド、ToDoを識別するフィールドidをもつ。
列挙PriorityはToDoの重要度を表す列挙であり、重要度が低い順にLOW、MIDDLE、HIGHである。
列挙StateはToDoの状態を表す列挙であり、NOT_YET_STARTEDは未着手、STARTEDは着手済み、DONEは完了を表す。
(2) クラスToDoListはToDoのリストを保持するクラスである。
リスト中に、フィールドidの値が同じToDoを複数個含まないことを保証する。
ToDoを追加するメソッドaddと、ToDoの更新を行うメソッドupdate、条件に合うToDoのリストを返すメソッドselectをもつ。
メソッドaddの引数に、既にリストに保持されているToDoを指定したとき、及びメソッドupdateの引数に、リストにないToDoを指定したときは何もしない。
メソッドselectの引数には、条件を0個以上指定できる。条件を指定したときは、全ての条件に合致するToDoから成るリストを返す。条件を指定しないときは、保持する全てのToDoから成るリストを返す。
(3) インタフェースConditionは、ToDoを選択する際の条件を表すクラスが実装するインタフェースである。メソッドtestは条件に合致するときにtrueを返す。
(4) クラスToDoListTesterは、テスト用のクラスである。




設問1:
問題文を見る プログラム中のに入れる正しい答えを、解答群の中から選べ。
aに関する解答群
ア:((ToDo) o).id.equals(id)
イ:(ToDo) o.id.equals(id)
ウ:id.equals(id)
エ:o.id.equals(id)
bに関する解答群
ア:!todoList.contains(todo)
イ:!todoList.isEmpty()
ウ:todoList.contains(todo)
エ:todoList.isEmpty()
cに関する解答群
ア:!= -1
イ:< todoList.size()
ウ:== -1
エ:>= todoList.size()
dに関する解答群
ア:boolean selected = false
イ:boolean selected = true
ウ:int selected = 0
エ:int selected = todoList.size()
eに関する解答群
ア:+=
イ:=
ウ:==
エ:|=
模範解答
a:ア、b:ア、c:ア、d:イ、e:イ
解説
解答の導き方
この問題では、ToDoを「どのToDoか」を見分ける方法と、条件に合うToDoを選ぶ方法を考えます。特に、リスト内での検索には、ToDoのidが使われます。
a:ToDoが同じかどうかの判定
プログラムの説明では、ToDoを識別するフィールドとしてidをもつとあります。したがって、主題や期限などが同じかではなく、idが同じかどうかで同じToDoと判定する必要があります。
equalsの引数oは、どの種類のデータも受け取れる型です。そのため、まずoがToDoであることを確かめてから、ToDoに変換してidを比較します。該当する形は ((ToDo) o).id.equals(id) です。
ここでは、&&の左側がfalseなら右側を評価しません。したがって、oがToDoでない場合に、ToDoへの変換やidの参照を行わずに済みます。
b:既にリストにあるToDoを追加しない
プログラムの説明には、リスト中にidが同じToDoを複数個含まないとあります。
リストのcontainsは、指定したToDoと同じものがリストにあるかを調べます。この判定には、先ほどのequalsが使われます。ですから、まだリストにない場合だけ追加すればよく、条件は !todoList.contains(todo) です。!は「〜ではない」という意味です。
追加時にはnew ToDo(todo)でコピーを作っていますが、コピー用のコンストラクタは元のToDoと同じidを引き継ぎます。そのため、コピーしても同じToDoとして判定できます。
c:リストにあるToDoだけを更新する
indexOfは、指定したToDoがリストの何番目にあるかを返します。見つからない場合は-1を返します。
そのため、更新してよいのは、検索結果が-1ではない場合です。条件は != -1 です。見つからないToDoを更新しようとすると、リストの存在しない位置を置き換えることになってしまいます。
d:条件が0個のときも全て選ぶ
selectでは、渡された条件を全て満たすToDoを選びます。ところが、条件が0個のときは、プログラムの説明どおり保持する全てのToDoを返さなければなりません。
そこで、条件の確認を始める時点では「選択できる」としておきます。条件が0個なら、その値を変える処理がないため、全てのToDoが選ばれます。したがって、dには boolean selected = true が入ります。
e:条件の判定結果をselectedに入れる
条件を1つずつ確認するときは、条件の判定結果をselectedに代入します。条件を満たせばtrue、満たさなければfalseになります。
falseになった場合は、直後のif文でループを抜けます。したがって、次の条件によって結果が上書きされることはありません。eに入るのは、値を代入する = です。
プログラム4の実行結果を確かめる
list.select()は条件を指定していないので、リスト内の全てのToDoを返します。返されたToDoはコピーですが、元と同じidをもっています。各コピーの状態をSTARTEDに変更してupdateすると、idによってリスト内の対応するToDoが見つかり、変更後のToDoに置き換わります。
一方、PC購入のToDoは新しいidをもつため、リスト内にはありません。indexOfの結果は-1となり、updateでは何も行われません。
最後のselectでは、次の2つの条件を同時に満たすToDoを選びます。
- 期限が20160501より前であること
- 重要度がHIGHであること
期限の文字列は年、月、日の順に並んでいるため、この問題の比較では、20160501より前の日付を文字列の比較で判定できます。条件に合うのは、メール送信と報告書作成です。状態はいずれも、それまでの更新でSTARTEDになっています。
誤りやすいポイント
- equalsで主題や期限を比べるのではなく、ToDoを識別するidを比べます。
- containsは、単に同じ実体があるかだけでなく、equalsによる判定で同じToDoがあるかを調べます。
- indexOfで見つからないときの値は-1です。リストの要素数と比較するのではなく、-1かどうかを確認します。
- selectedをfalseで初期化すると、条件が0個の場合に全てのToDoを選べません。
- eの位置では、条件判定の結果を代入します。==は値の比較なので、ここでは使いません。
- updateに指定したToDoがリストにない場合は、追加ではなく何もしません。
FAQ
Q: 条件が複数あるとき、なぜselectedに条件の判定結果を代入するだけでよいのですか?
A: 条件を満たさなかった時点でループを抜けるからです。そこまでの条件を全て満たした場合だけ次の条件へ進み、どれか1つでも満たさなければ選ばれない仕組みです。
Q: selectで返したToDoの状態を変えただけでは、リストの中身も変わりますか?
A: このプログラムでは、selectの結果にToDoのコピーを追加しています。そのため、返されたコピーの状態を変更しただけでは、リスト内のToDoは変わりません。変更を反映するには、updateを呼び出します。
Q: 期限を文字列のまま比較できるのはなぜですか?
A: この問題では、年月日が年、月、日の順に並ぶ形式です。そのため、期限の文字列を順に比較すると、日付の前後関係を判定できます。
関連キーワード: equalsメソッド、リスト検索、可変長引数、論理積、文字列比較
設問2:
問題文を見る プログラム4の実行結果を図1に示す。に入れる正しい答えを、解答群の中から選べ。

f, gに関する解答群
ア:主題: PC購入, 期限: 20160531, 優先度: HIGH
イ:主題: 会議室予約, 期限: 201605301200, 優先度: HIGH
ウ:主題: チケット購入, 期限: 20160430, 優先度: MIDDLE
エ:主題: 報告書作成, 期限: 20160428, 優先度: HIGH
オ:主題: ホテル予約, 期限: 20160420, 優先度: LOW
カ:主題: メール送信, 期限: 201604181500, 優先度: HIGH
模範解答
f:カ、g:エ
解説
解答の導き方
-
まず、リストに入るToDoを確認します。プログラムでは、最初に「メール送信」「ホテル予約」「チケット購入」「報告書作成」「会議室予約」の5件を追加しています。その後に「PC購入」を引数にしてupdateを呼び出していますが、updateは新しいToDoを追加するメソッドではありません。リスト内に同じidのToDoがある場合に、その内容を更新します。「PC購入」は新しく作られたToDoなので、リスト内のToDoとidが一致せず、更新されません。したがって、この時点でもリストは最初の5件のままです。
-
次に、5件の状態がどうなるかを追います。引数を指定しないselect()は、リストに保持している全てのToDoを返します。ただし、返されるのはリスト内のToDoそのものではなくコピーです。ループ内では、そのコピーの状態をSTARTEDに変更し、続けてupdateを呼び出しています。コピーは元のToDoと同じidを持つので、updateによってリスト内の該当するToDoも更新されます。この処理を5件全てに行うため、リスト内の5件の状態はすべてSTARTEDになります。
-
最後に、condition1とcondition2の両方を満たすToDoを探します。問題文では、条件を複数指定した場合、「全ての条件に合致するToDo」を返すと説明されています。
- condition1は、期限が「20160501」より前であることを調べます。
- condition2は、重要度がHIGHであることを調べます。
期限は年、月、日、必要に応じて時分の順に並んでいます。そのため、この問題では期限の文字列を比較することで、日付の前後を判定できます。条件を両方満たすのは「メール送信」と「報告書作成」です。選択結果はリストに入っている順に処理されるため、図1の上段のfは「メール送信」に当たるカ、下段のgは「報告書作成」に当たるエです。
誤りやすいポイント
- updateを、リストにないToDoも追加するメソッドだと考えないようにします。「PC購入」はリストに追加されません。
- select()が返すToDoはコピーです。コピーの状態を変更しただけではリスト内の状態は変わらず、updateを呼び出して反映する必要があります。
- condition1とcondition2は、どちらか一方を満たせばよいのではありません。両方を満たすToDoだけが選ばれます。
- 条件に合うToDoを期限順に並べ替える処理はありません。表示される順番は、リストに追加された順です。
FAQ
Q: 「メール送信」の期限は12桁ですが、「20160501」と比較できますか?
A: 比較できます。「メール送信」の期限は「201604」から始まり、「201605」より前の日付です。そのため、期限の条件を満たします。
A: 比較できます。「メール送信」の期限は「201604」から始まり、「201605」より前の日付です。そのため、期限の条件を満たします。
Q: select()で取得したToDoの状態を変えた後、なぜupdateが必要なのですか?
A: select()が返すのは、リスト内のToDoのコピーだからです。コピーで行った変更をリストに反映するために、updateを呼び出します。
A: select()が返すのは、リスト内のToDoのコピーだからです。コピーで行った変更をリストに反映するために、updateを呼び出します。
Q: 重要度がHIGHなら、期限に関係なく表示されますか?
A: いいえ。期限の条件と重要度の条件の両方を満たす必要があります。「会議室予約」は重要度がHIGHですが、期限が「20160501」より後なので選ばれません。
A: いいえ。期限の条件と重要度の条件の両方を満たす必要があります。「会議室予約」は重要度がHIGHですが、期限が「20160501」より後なので選ばれません。
関連キーワード: equalsメソッド、リスト検索、オブジェクトのコピー、文字列比較、論理積



