Java:任意の順番でのソート

JavaのCollection型(サブクラスにList型等がある)は、Collections.sortメソッドでソートすることが可能です。
Collections.sortメソッドは、第一引数にソートしたいCollection型のオブジェクト、第二引数にComparator型のオブジェクトを渡します。
第二引数のComparator型のオブジェクトにより、ソート順が決まります。

Collections.sortメソッドでは、Comparator型のcompareメソッドを利用してソートを行います。
ソート順を定義するためには、Comparator型を実装したクラスを独自に作成し、compareメソッドをオーバーライドして独自のロジックを記述する必要があります。
compareメソッドの説明は以下です。
https://docs.oracle.com/javase/jp/8/docs/api/java/util/Comparator.html#compare-T-T-
簡単に言うと、compareメソッドの第一引数と第二引数について、戻り値がマイナスの値の場合は「第一引数→第二引数」の順番に並び、戻り値がプラスの値の場合は「第二引数→第一引数」の順番に並びます。

これを利用して、任意の順番でソートを行うサンプルが以下になります。

【サンプルコード】

・MySortMain.java

・MySort1.java

・MySort2.java

【実行結果】


いかがでしたでしょうか。

javaで独自順のソート処理をする機会があったので、実装方法を簡単に書いてみました。
Comparatorインターフェースのcompareメソッドを実装すること、compareメソッドの戻り値によりソート順を定義すること、を抑えれば、何をしているか理解できると思います。

Base64の説明とjavaでのエンコード・デコードの例

Base64とは、テキストデータやバイナリデータ(画像ファイル、PDFファイル等)を表現する方式の一つです。
以下の64種類の文字と末尾のパディング文字(=)を用いて表現するのが特徴です。
・アルファベット(a~z, A~Z)
・数字(0~9)
・一部の記号(+, /)

この表現方式は、一部の文字しか使用できないプロトコルでデータの送受信を行いたい場合に有用であり、ASCII文字しか送受信できないプロトコルでもデータの送受信が可能になります。
有名な所で言うと、電子メール(SMTP)でBase64が使われています。
また、APIでのデータ送受信でも良く使われます。

ここで、Base64のエンコード・デコードをイメージしやすくするため、2バイト文字(日本語)をJava8 の java.util.Base64を用いてエンコード・デコードしてみます。

【サンプルコード】

・Base64Test.java

【実行結果】


なお、今回の記事では触れませんが、バイナリファイルを1バイトずつエンコード・デコードすることで、バイナリデータにも対応できます。
また、Base64のエンコード・デコードの方式は公開されており、人気の言語ではライブラリも存在しているので、他の言語でも実装可能です。


いかがでしたでしょうか。

Base64については入門書で触れられることが少なく、情報処理技術者試験でも午前問題で稀に出題される程度なので、実務で初めて耳にする方は少なくないと思います。
「Base64を使えば画像や動画もASCII文字(1バイト文字)で表現できる」というざっくりな知識だけでも覚えておけば、実務でもスムーズにコミュニケーションできるかと思います。

java:通常のクラスと匿名クラス(無名クラス)の書き方の比較

以前の記事で「通常のクラスと匿名クラスの書き方の比較を見たい」という意見があったので、少し補足を行います。

サンプルコードの通り、匿名クラスの場合、クラスに名前を付ける必要が無く、またオブジェクト生成と同時に処理を定義することができます。
これにより、以下のようなメリットがあります。

  • 記述を簡潔にできる
  • 使い捨てのクラスを定義でき、使い捨てであるという意図も明確にできる
  • (ソースファイル名とクラス名を同じにする場合)管理が容易になる

【サンプルコード】

・AnonymousClass.java

・AnonymousSubClass.java

・AnonymousInterface.java

・AnonymousImplementsClass.java

・AnonymousMain.java

【実行結果】


良い意見やコメントを頂いた時は、今回のような形で答えていきたいと思います!

java:引数で渡した参照型変数をメソッド内で変更する書き方について

参照型変数を引数としてメソッドに渡し、呼び出し先のメソッドの中でその参照型変数に変更を入れた場合、呼び出し元でもその変更内容を参照することができます。
ソースコードで言うと、以下のような形で呼び出し元に影響を与えることができます。
(isDisplay[0]の変更は呼び出し元にも反映されます。実行すると、”Hello World!”が出力され、”Error!”は出力されません。)

このような書き方は、C言語のレガシーなコードではよく見かけますが、javaではあまり見かけることがないと思います。
レガシーな現場に入ったことが無い方は、見慣れていない、違和感がある、と感じる方が多いと思います。

javaでは、以下のように戻り値により呼び出し元に影響を与える書き方の方が主流です。

ビジネスロジックを知り尽くしている場合は、前者のようにメソッドに参照型変数を渡してメソッドの中で好きに変更させる形にした方が楽にコーディングできます。
しかし、ビジネスロジックをよく知らない担当者がソースコードを調査したり改修したりする場合、後者のように戻り値を通して呼び出し元に影響を与える形にしないと、読む必要があるソースコードの範囲が広がり、調査・改修が困難になります。
今回の例で言うと、”isDisplay[1]”に変更が入っていないことを知るために、前者のコードではメソッドの中まで見る必要がありますが、後者のコードではメソッドの中まで見る必要はありません。

現在の開発現場では、複数の担当者が入れ替わり立ち替わりでソースコードを改修し続けるのが主流ですので、後者の書き方の方が望まれます。
現行のソースコードが既に前者の形で書かれているのであれば、それに合わせた方が良い場合もありますが、そうではないなら後者の書き方で書くべきでしょう。


いかがでしたでしょうか。

新しい技術に触れることが多い技術者が前者のようなソースコードを見て戸惑っていたので、今回の記事を書きました。
今からソースコードを書くなら後者の書き方を推奨するのですが、前者のような書き方もあるというのは覚えておいて損は無いかもしれません。

java:例外発生後に例外のメッセージを書き変える

例外オブジェクトにはメッセージが格納されており、getMessage()メソッドでそのメッセージを取得することができます。
しかし、このメッセージはコンストラクタでのみ設定可能であり、メッセージを後で変更するメソッドは用意されていないため、例外クラスに用意されている手段では例外発生後にメッセージを書き変えることはできません。

しかし、リフレクションを使用することで、メッセージを後で書き変えることが可能です。
メッセージはThrowableクラスのprivateのクラス変数”detailMessage”に保持されるため、これをリフレクションで書き変えます。

サンプルコードは以下の通りです。

【サンプルコード】

・ExceptionTest.java

【実行結果】

・コンソール(標準出力)

・コンソール(標準エラー出力)


ちなみに、リフレクション時に発生してしまう標準エラー出力ですが、(試していませんが)Java8では出ないようです。
筆者の環境(Java10)だと出てしまいます。

そこで以下の2つの手段を試しましたが、何れもエラー抑止はできませんでした。

1.JVM引数で抑止

“–illegal-access=deny”を指定したら逆に異常終了するようになってしまいました。

2.標準エラー出力の出力ストリームを変更

通常、”System.setErr(自作PrintStreamオブジェクト);”を記述することで標準エラー出力の出力先を変更し、コンソールに出力されないようにすることができるのですが、今回のケースではそれができませんでした。
“System.err.close();”を記述した場合はコンソールへの出力を抑止することができたので、リフレクション時のWARNINGメッセージでは”System.err”を直接使用しているのではないかと思います。
なお、”System.err”はクローズしたらリオープンすることができないので、”System.err.close();”を実行してしまうと、以降は予期せぬ例外等が発生しても標準エラー出力として出力できなくなってしまいます。
(”System.err.close();”を実行した後に”System.setErr(System.out);”を実行して、予期せぬ例外等も全て標準出力として出力することならできますが…)


いかがでしたでしょうか。

今回の記事では、例外発生時のメッセージを後から書き変えられることを確認しましたが、標準エラー出力が出力されることを考えると、このようなソースコードはできる限り書くべきではないと思います。
どうしても書き変え可能な変数を伝播させたいのであれば、独自例外クラスに独自のクラス変数を定義してそれを伝播させる等の工夫をするべきだと思います。