UTF-8のテキストファイルを、SJISのテキストファイルに変換 [Java]
せっかくなので、Java版も作ってみました。
UTF-8のテキストファイルを、SJISのテキストファイルに変換しています。
ProgressMonitorの使い方 [Java]
ProgressSample.java
ProgressMonitorはJProgressBarを実装するよりも、お手軽に処理の進捗度を表示するのに便利です。
まずはそのまま愚直に実装してみたら、処理の実行中に中身が表示されないダイアログが出力され、処理が終わるまで何もできないという、期待とは全く違った動作になってしまいました。
なるほど、別スレッドにしないとダメなんだなと思い、Runnableで実装してみましたが結果は同じでした。どうやらJava Swingで別スレッドを立てるためには別の方法を考えなければならないようです。。。
そこからいろいろと試行錯誤してみると、Java Swingで別スレッドを立てるために特化した、SwingWorkerという(抽象)クラスを使用すると、期待する結果が得られそうなことが分かってきました。
早速、実装してみると・・・動きました!!
期待通りの結果になりました。
SwingWorkerのインスタンス化で指定するGenericsは、一つ目が処理結果の型、二つ目が途中経過の型とのことですが、今回は特に意識していないので、Objectを指定しました。
SwingWorkerの必須メソッドはdoInBackground()です。ここには、別スレッドで処理したい内容を実装します。名前の通りですね。
SwingWorkerには、done()という処理終了時に実行されるメソッドが用意されています。
今回の例で言うと、doInBackground()の処理中に、呼び出し元のボタンを操作されたくなかったので、冒頭でbtn.setEnabled(false)とし、処理の開始時にボタンを非活性にしてみました。処理の終了時はdone()でbtn.setEnabled(true)とし、ボタンを元に戻すようにしています。
SwingWorkerの匿名インナーは以上で、インスタンス化したSwingWorkerをexecute()で直接実行しています。他にも途中経過を取得するためのメソッドなどが用意されていましたが、今回やりたかったことに必要なかったので未実装です。
さて、これでProgressMonitorを動かす準備が整いました。まずはインスタンスを生成しましょう。Integer.MAX_VALUE / 10で最大件数を定義しておきます。ProgressMonitorのコンストラクタでは第一引数は親コンポネント。第二引数は説明用のメッセージ(不変)、第三引数は処理の状態を説明する短い注、第四引数は範囲の下限、第五引数は範囲の上限をそれぞれ指定するとJavadocにあります。
最大件数分のループ処理部分で、ProgressMonitorの更新処理を定義していきます。isCanceled()は処理中にダイアログの取消ボタンが操作されたときにtrueを返すので、return nullとし、処理を中断します。
ちなみに最大件数をInteger.MAX_VALUE / 10(=214,748,364)と定義しているのは、件数が大き過ぎるとなかなか処理が終わらなく、管理人PCの適正値が10分の1程度だったからです。
次に、表示内容の更新部分です。if (i % MAX_SIZE == 0)とし、1000件毎に情報を更新しているのは、1件ずつ更新していると、なかなか処理が終わらないからで特に大意はありません。setProgressでプログレスバーの更新、setNoteで処理の状態をそれぞれ更新しています。今回は百分率で表示したかったので、ムダに演算処理をかませています。
ループ処理が終了したら後片付けです。といってもclose()を呼び出すだけですが、setProgressが範囲の上限に達しなかった場合には、ダイアログが残り続けてしまうため、今回の例では必要になります。
といったところで、簡単にProgressMonitorを使用するためのコードが書けるわけですが、実際に実行してみると、情報の更新処理がボトルネックになってしまうという、残念な結果になってしまいました。これは、更新タイミングの粒度を調整することで、ある程度対応できるとは思うのですが、粒度が荒くなるほど、プログレスバーのカクカク感が増します。
時間が掛かる処理の進捗度を確認したいのに、それを表示させるためには、さらに時間が掛かるというジレンマを抱えることになっちゃうから、使い処が難しいなぁなんて思っていたのですが、よくよく考えてみたら今回の例は2億回を超えるループ処理なので、日常で扱う程度の件数なら問題ないんですね。むしろ、処理が速すぎてダイアログすら表示されないことの方が多いのかも。。。
何にせよ、Java Swingで別スレッドを立てるという、別の目的も達成できたので良しとしました。
ProgressMonitorはJProgressBarを実装するよりも、お手軽に処理の進捗度を表示するのに便利です。
まずはそのまま愚直に実装してみたら、処理の実行中に中身が表示されないダイアログが出力され、処理が終わるまで何もできないという、期待とは全く違った動作になってしまいました。
なるほど、別スレッドにしないとダメなんだなと思い、Runnableで実装してみましたが結果は同じでした。どうやらJava Swingで別スレッドを立てるためには別の方法を考えなければならないようです。。。
そこからいろいろと試行錯誤してみると、Java Swingで別スレッドを立てるために特化した、SwingWorkerという(抽象)クラスを使用すると、期待する結果が得られそうなことが分かってきました。
早速、実装してみると・・・動きました!!
期待通りの結果になりました。
SwingWorkerのインスタンス化で指定するGenericsは、一つ目が処理結果の型、二つ目が途中経過の型とのことですが、今回は特に意識していないので、Objectを指定しました。
SwingWorkerの必須メソッドはdoInBackground()です。ここには、別スレッドで処理したい内容を実装します。名前の通りですね。
SwingWorkerには、done()という処理終了時に実行されるメソッドが用意されています。
今回の例で言うと、doInBackground()の処理中に、呼び出し元のボタンを操作されたくなかったので、冒頭でbtn.setEnabled(false)とし、処理の開始時にボタンを非活性にしてみました。処理の終了時はdone()でbtn.setEnabled(true)とし、ボタンを元に戻すようにしています。
SwingWorkerの匿名インナーは以上で、インスタンス化したSwingWorkerをexecute()で直接実行しています。他にも途中経過を取得するためのメソッドなどが用意されていましたが、今回やりたかったことに必要なかったので未実装です。
さて、これでProgressMonitorを動かす準備が整いました。まずはインスタンスを生成しましょう。Integer.MAX_VALUE / 10で最大件数を定義しておきます。ProgressMonitorのコンストラクタでは第一引数は親コンポネント。第二引数は説明用のメッセージ(不変)、第三引数は処理の状態を説明する短い注、第四引数は範囲の下限、第五引数は範囲の上限をそれぞれ指定するとJavadocにあります。
最大件数分のループ処理部分で、ProgressMonitorの更新処理を定義していきます。isCanceled()は処理中にダイアログの取消ボタンが操作されたときにtrueを返すので、return nullとし、処理を中断します。
ちなみに最大件数をInteger.MAX_VALUE / 10(=214,748,364)と定義しているのは、件数が大き過ぎるとなかなか処理が終わらなく、管理人PCの適正値が10分の1程度だったからです。
次に、表示内容の更新部分です。if (i % MAX_SIZE == 0)とし、1000件毎に情報を更新しているのは、1件ずつ更新していると、なかなか処理が終わらないからで特に大意はありません。setProgressでプログレスバーの更新、setNoteで処理の状態をそれぞれ更新しています。今回は百分率で表示したかったので、ムダに演算処理をかませています。
ループ処理が終了したら後片付けです。といってもclose()を呼び出すだけですが、setProgressが範囲の上限に達しなかった場合には、ダイアログが残り続けてしまうため、今回の例では必要になります。
といったところで、簡単にProgressMonitorを使用するためのコードが書けるわけですが、実際に実行してみると、情報の更新処理がボトルネックになってしまうという、残念な結果になってしまいました。これは、更新タイミングの粒度を調整することで、ある程度対応できるとは思うのですが、粒度が荒くなるほど、プログレスバーのカクカク感が増します。
時間が掛かる処理の進捗度を確認したいのに、それを表示させるためには、さらに時間が掛かるというジレンマを抱えることになっちゃうから、使い処が難しいなぁなんて思っていたのですが、よくよく考えてみたら今回の例は2億回を超えるループ処理なので、日常で扱う程度の件数なら問題ないんですね。むしろ、処理が速すぎてダイアログすら表示されないことの方が多いのかも。。。
何にせよ、Java Swingで別スレッドを立てるという、別の目的も達成できたので良しとしました。