基本情報技術者 2017年 秋期 午後 問11
次のJavaプログラムの説明及びプログラムを読んで、設問1~3に答えよ。
〔プログラムの説明〕
文字列の配列を要素にもつ配列で表現された表中の行を、並べ替えるプログラムである。表は行の配列として、行は文字列の配列として構成される。各行の指定した列が、指定した順序に並ぶように、行を並べ替える。
図1は、表の最左列の文字列を辞書順に、最左列の文字列が等しい行は右隣の列の文字列を数値とみなしたときに降順になるように並べ替えた例である。

クラスTableSorterは、表中の行を並べ替える機能を提供する。次のメソッドと入れ子クラスOrderByをもつ。
(1) putSortOrder(String key, Comparator<String> order) ―このクラスのメソッドsortで指定可能な順序を登録する。
key ― 表を並べ替える順序を示すために使用する文字列(以下、順序指定子という)。
order ― インタフェースComparator型のオブジェクトで、keyで指定した順序指定子が示す順序を規定する。
(2) sort(String[][] table, final OrderBy... orderBys) ―条件として指定した列が指定した順序になるように、表中の行を並べ替える。条件は複数指定できる。
並び順の決定には先に指定した条件が優先される。先に指定した条件で並び順が決まらない行については、後に指定した条件を加えて決定する。指定した全ての条件で順序が決まらない行については、並べ替える前の並び順を維持する。
table ― 整列対象の表を格納したString型の配列を要素にもつ配列を指定する。
orderBys ― 条件を必要なだけ指定する。
クラスTableSorterの入れ子クラスOrderByは並べ替えの条件を表すクラスである。フィールドkeyは順序指定子、フィールドcolは比較対象の列の位置を示す数(行を表すString型配列の添字)である。フィールドisReversedがfalseであれば、この条件が表す順序が順序指定子keyで指定されるオブジェクトのメソッドcompareが返す大小関係での昇順で並べ替えることを意味し、trueであれば、降順で並べ替えることを意味する。
クラスTableSorterTesterはテスト用のプログラムである。
実行結果を図2に示す。



設問1:
問題文を見る プログラム1中のに入れる正しい答えを、解答群の中から選べ。
aに関する解答群
ア:Character, Comparator<String>
イ:Character, String
ウ:String, Comparator<String>
エ:String, String
bに関する解答群
ア:int i = 0; i < orderBys.length; i++
イ:int i = 0; i < table.length; i++
ウ:OrderBy orderBy : orderBys
エ:String[] row : table
cに関する解答群
ア:.get(col)
イ:.get(orderBy.col)
ウ:[col]
エ:[orderBy.col]
dに関する解答群
ア:!=
イ:<
ウ:==
エ:>
eに関する解答群
ア:-order
イ:order
ウ:orderBy.isReversed ? -order : order
エ:orderBy.isReversed ? order : -order
模範解答
a:ウ、b:ウ、c:エ、d:ア、e:ウ
解説
解答の導き方
このプログラムは、表の行を複数の条件で比べ、先に指定した条件で決まらないときに次の条件で比べます。各空欄について、どの値を使って比べるのかを確認します。
a:順序指定子と比較方法を対応付ける型
orderMapは、順序指定子を使って、その順序で比較するためのオブジェクトを取り出すMapです。Mapは、キーと値を対応付けて保存します。
このプログラムでは、キーは順序指定子のString型、値は文字列を比較するComparator<String>型です。Comparatorは、二つの値を比較して大小関係を返す仕組みです。
したがって、型の組合せは次のとおりです。
String, Comparator<String>
解答群ではウです。
b:指定された比較条件を順に調べる
問題文には「条件は複数指定できる」とあり、先に指定した条件で並び順が決まらない場合には、後の条件を使います。そのため、繰り返す対象は表の行ではなく、条件を格納したorderBysです。
orderBysの各要素はOrderBy型なので、繰返しの形は次のとおりです。
OrderBy orderBy : orderBys
解答群ではウです。この繰返しの中で、現在調べている条件をorderByとして扱います。
c:比較する列を選ぶ
OrderByのフィールドcolは、比較対象の列の位置を表します。また、s1とs2はそれぞれ比較する行で、String型の配列です。配列の要素は添字を使って取り出すので、orderBy.colを添字にします。
s1[orderBy.col], s2[orderBy.col]
解答群ではエです。getは配列ではなく、Mapなどの値を取り出すときに使うメソッドです。
d:次の条件に進むか決める
Comparatorのcompareは、比較結果として、負の値・0・正の値を返します。0は、二つの値がこの条件では同じ順序だという意味です。
比較結果が0以外なら、この条件だけで行の順序が決まるため、その結果を返します。0なら順序が決まらないので、次の条件に進みます。
したがって、判定は次の形になります。
order != 0
解答群ではアです。
e:昇順か降順かを反映する
isReversedがfalseなら、Comparatorが返した大小関係のまま並べます。trueなら、比較結果の符号を反対にして、順序を逆にします。
そのため、isReversedがtrueのときだけorderにマイナスを付けます。
orderBy.isReversed ? -order : order
解答群ではウです。
テスト用プログラムでは、最初に「lex」の条件で0列目を辞書順に比べ、次に「num」の条件で1列目を数値として比べます。2番目の条件はisReversedがtrueなので、数値の降順です。たとえば、bananaの行同士は1列目の値を比べ、2を1より先に並べます。これが図1と図2の並び順につながります。
すべての条件で比較結果が0なら、最後に0を返します。オブジェクト配列を比較関数で並べ替えるArrays.sortは安定ソートなので、すべての条件で同じ順序になる行は、並べ替え前の順序が保たれます。
誤りやすいポイント
- Mapの型は、キーと値の順に指定します。この問題では、キーがString、値がComparator<String>です。
- 繰り返すのは表の行ではなく、指定された条件orderBysです。条件を一つずつ調べ、比較結果が0なら次へ進みます。
- 行はString型配列なので、列の値は添字を使って取り出します。配列の要素を取り出す書き方は角括弧です。
- 比較結果が0以外なら、負の値でも正の値でも順序は決まっています。そのため、判定は「0より大きいか」や「0より小さいか」ではなく、「0と等しくないか」です。
- 降順にするために符号を逆にするのは、isReversedがtrueのときだけです。常に符号を逆にすると、昇順と降順が入れ替わってしまいます。
FAQ
Q:比較結果が0のとき、なぜすぐに0を返さないのですか?
A:その条件だけでは順序が決まらないためです。後に指定された条件も使って、二つの行の順序を決めます。
A:その条件だけでは順序が決まらないためです。後に指定された条件も使って、二つの行の順序を決めます。
Q:orderBy.colは、何を指定していますか?
A:比較する列の位置です。たとえば、0なら各行の先頭の列、1ならその右隣の列を表します。
A:比較する列の位置です。たとえば、0なら各行の先頭の列、1ならその右隣の列を表します。
Q:すべての条件で比較結果が0の場合、行はどうなりますか?
A:この比較処理は0を返します。Arrays.sortの安定ソートによって、並べ替え前の行の順序が保たれます。
A:この比較処理は0を返します。Arrays.sortの安定ソートによって、並べ替え前の行の順序が保たれます。
関連キーワード: Comparator、ジェネリクス、可変長引数、配列、安定ソート
設問2:
問題文を見る プログラム2のαで示した2行を次の2行と入れ替えて実行したとき、実行結果の1行目に出力される内容として正しい答えを、解答群の中から選べ。
sorter.sort(data, new TableSorter.OrderBy("lex", 2),
new TableSorter.OrderBy("lex", 0));
解答群
ア:apple 2 300
イ:apple 3 1,000
ウ:banana 1 300
エ:banana 2 2,000
オ:cherry 1 1,000
模範解答
イ
解説
解答の導き方
置き換え後の指定では、最初に列の位置「2」を「lex」で並べ、その順序で決まらない行を列の位置「0」の「lex」で並べます。
Javaの配列の添字は「0」から始まるため、列の位置「2」は3列目、「0」は1列目です。また、OrderByの2つの引数を使う指定では、順序は昇順です。
「lex」は文字列を辞書順に比べる順序です。文字列を比較する処理では、各文字を先頭から比べます。したがって、3列目の値は数値としてではなく、文字列として次の順に並びます。
「1,000」と「2,000」と「300」は、先頭の文字がそれぞれ「1」「2」「3」です。そのため、「1,000」、「2,000」、「300」の順になります。
次に、3列目の値が同じ行を1列目で比べます。問題文では、先に指定した条件で並び順が決まらない行に、後の条件を加えて決めると説明されています。「1,000」の行は1列目が「apple」と「cherry」なので、「apple」が先です。
よって、並べ替え後の先頭行は「apple 3 1,000」です。選択肢ではイに当たります。
誤りやすいポイント
- 「300」と「2,000」を数値として比べないようにします。今回は「lex」で文字列として比べるため、「2,000」が「300」より先になります。
- 列の位置「2」は2列目ではなく3列目です。配列の添字が「0」から始まるためです。
- 置き換え後の条件では、2列目を数値順に比べる指定はありません。最初の条件は3列目、次の条件は1列目の文字列比較です。
- 1列目の比較は、3列目が同じ行の並び順を決めるときに使います。まず3列目の条件が優先されます。
FAQ
Q: 「300」のほうが数値として小さいのに、なぜ「2,000」より後になるのですか?
A: 今回は数値としてではなく、文字列として比べるからです。先頭の文字を比べると「2」は「3」より前なので、「2,000」が先になります。
A: 今回は数値としてではなく、文字列として比べるからです。先頭の文字を比べると「2」は「3」より前なので、「2,000」が先になります。
Q: 「1,000」の行では、なぜ「apple 3 1,000」が先になるのですか?
A: 3列目が同じなので、次の条件である1列目を比べます。「apple」は「cherry」より辞書順で先です。
A: 3列目が同じなので、次の条件である1列目を比べます。「apple」は「cherry」より辞書順で先です。
Q: 列の位置「2」は、なぜ3列目を指すのですか?
A: 配列では最初の要素の位置を「0」と数えるためです。位置「0」が1列目、位置「1」が2列目、位置「2」が3列目です。
A: 配列では最初の要素の位置を「0」と数えるためです。位置「0」が1列目、位置「1」が2列目、位置「2」が3列目です。
関連キーワード: 配列、添字、文字列比較、辞書順、複数条件ソート
設問3:
問題文を見る プログラム2のαで示した2行を次の8行と入れ替えて実行したとき、実行結果の3行目に出力される内容として正しい答えを、解答群の中から選べ。
sorter.putSortOrder("numC", new Comparator<String>() {
public int compare(String s1, String s2) {
return new Integer(s1.replace(",", "")).
compareTo(new Integer(s2.replace(",", "")));
}
});
sorter.sort(data, new TableSorter.OrderBy("numC", 2),
new TableSorter.OrderBy("lex", 0));
解答群
ア:apple 2 300
イ:apple 3 1,000
ウ:banana 1 300
エ:banana 2 2,000
オ:cherry 1 1,000
模範解答
イ
解説
解答の導き方
今回の変更後のプログラムは、まず数値として小さい順に並べ、数値が同じ行は文字列の順に並べます。
-
並べ替え条件と対象の列を確認します。
追加された順序指定子「numC」は、カンマを取り除いてから整数として比較する順序です。また、sortには「numC」の列の位置として「2」が指定されています。列の位置は0から数えるので、比較対象は各行の3列目です。次に指定されている「lex」は、すでに登録されている文字列比較の順序です。列の位置は「0」なので、各行の1列目を比較します。どちらのOrderByにも降順の指定はないため、どちらも昇順です。 -
1つ目の条件で、3列目を数値として比べます。
「1,000」や「2,000」は、そのままでは整数として比較できません。そのため、比較する前にカンマを取り除き、「1,000」は「1000」、「2,000」は「2000」として扱います。カンマを取り除くのは比較のためで、表にある文字列自体を書き換える処理ではありません。各行の3列目を比較用の数値にすると、次のようになります。まず、比較値が小さい順に並べると、300の行、1000の行、2000の行の順になります。 -
1つ目の条件だけで順番が決まらない行を、2つ目の条件で比べます。
3列目が300の行どうしは、1列目を「lex」で比べます。「apple」は「banana」より前なので、appleの行が先です。同じように、3列目が1000の行どうしでは、「apple」が「cherry」より前です。したがって、並べ替え後の行は次の順になります。3行目に出力されるのは「apple 3 1,000」です。解答群では イ です。
誤りやすいポイント
- 3列目を文字列のまま比べるのではありません。「numC」はカンマを取り除き、数値として比較します。
- 「1,000」を比較用に「1000」としても、出力される表の値は「1,000」のままです。
- 2つ目の条件「lex」は、1つ目の条件で順番が決まらない行どうしに使います。まず1列目で全体を並べるわけではありません。
- 列の位置「2」は3列目を指します。列の位置を0から数える点に注意してください。
FAQ
Q: 3列目が同じ行は、元の並び順のままですか?
A: 2つ目の条件「lex」も指定されているため、まず1列目を比べて順番を決めます。すべての条件で順番が決まらない場合は、問題文の説明どおり、並べ替える前の順番が維持されます。
A: 2つ目の条件「lex」も指定されているため、まず1列目を比べて順番を決めます。すべての条件で順番が決まらない場合は、問題文の説明どおり、並べ替える前の順番が維持されます。
Q: 「1,000」をカンマ付きのまま整数にできないのですか?
A: このプログラムでは、整数に変換する前にカンマを取り除いています。そのため、比較時には「1000」として扱えます。
A: このプログラムでは、整数に変換する前にカンマを取り除いています。そのため、比較時には「1000」として扱えます。
Q: 条件を2つ指定したとき、どちらが優先されますか?
A: 先に指定した条件が優先されます。この問題では「numC」が先なので、3列目の数値が最初の並べ替え基準です。
A: 先に指定した条件が優先されます。この問題では「numC」が先なので、3列目の数値が最初の並べ替え基準です。
関連キーワード: 配列、比較関数、数値比較、辞書順、安定ソート



