Java8で指定回数繰り返し

Java8からStreamAPIが利用できる。
StreamAPIを利用するとC言語の頃から利用されている下記のようなforループは書く必要がない。

for (int i = 0 ; i < 10 ; i++) {
    proc(i);
}

IntStreamを利用する

import java.util.stream.IntStream

IntStream.range(0, 10).forEach(i -> proc(i));

これで同様の処理が行える。
0から9までの10回。見慣れた感じだ。
配列の添え字は基本的に0から使うので、これで全く問題ない。

1〜10の場合は

出力用に1~10が欲しいときもある。従来の書き方であればこうだろうか。

for (int i = 1 ; i <= 10 ; i++) {
    System.out.println(i);
}

これで、1~10が出力される。StreamAPIを使うと

import java.util.stream.IntStream

IntStream.rangeClosed(1, 10).forEach(i -> {
    System.out.println(i);
});

rangeClosedを使うことで「10まで」と言うのがわかりやすく書ける。

まとめ

記述する文字数はStreamAPIを使った方が長くなってしまう気もするけど、やりたい事が伝わりやすいのはStreamAPIのような気がする。
開発標準がJava8以降になったら、順次導入していきたい。
(いまだにJava6が標準のシステムもメンテナンスしている)

同じタグの記事
同じカテゴリの記事

もうすぐ新元号(続報)

もうすぐ新元号。新元号も決まり、Java Runtimeがアップデートした。
それぞれのJava Runtimeで実行してみる。

サンプルプログラム

import java.util.Date;
import java.util.Locale;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

public class Wareki {
    public static void main(String[] args) {
        System.out.println(System.getProperty("java.version"));
        toWareki("1989/01/07");
        toWareki("1989/01/08");
        toWareki("2019/04/30");
        toWareki("2019/05/01");
    }

    public static void toWareki(String seireki) {
        try {
            DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, Locale.JAPAN);
            Date date = df.parse(seireki);
            SimpleDateFormat sdf = new SimpleDateFormat("GGGGyyyy.M.d", new Locale("ja", "JP", "JP"));
            System.out.println(seireki + ":" + sdf.format(date));
        } catch (Exception e) {
            System.out.println("Err." + e.getMessage());
        }
    }
}

平成の変わり目をjava.versionと供に出力する。

実行

Java 6

1.6.0_45
1989/01/07:昭和64.1.7
1989/01/08:平成元.1.8
2019/04/30:平成31.4.30
2019/05/01:平成31.5.1

Java 7

1.7.0_79
1989/01/07:昭和64.1.7
1989/01/08:平成元.1.8
2019/04/30:平成31.4.30
2019/05/01:平成31.5.1

1.7.0_80
1989/01/07:昭和64.1.7
1989/01/08:平成元.1.8
2019/04/30:平成31.4.30
2019/05/01:平成31.5.1

Java 8

1.8.0_191
1989/01/07:昭和64.1.7
1989/01/08:平成元.1.8
2019/04/30:平成31.4.30
2019/05/01:平成31.5.1

1.8.0_201
1989/01/07:昭和64.1.7
1989/01/08:平成元.1.8
2019/04/30:平成31.4.30
2019/05/01:平成31.5.1

1.8.0_211
1989/01/07:昭和64.1.7
1989/01/08:平成元.1.8
2019/04/30:平成31.4.30
2019/05/01:令和元.5.1

Java 11

11
1989/01/07:昭和64.1.7
1989/01/08:平成元.1.8
2019/04/30:平成31.4.30
2019/05/01:元号元.5.1

11.0.1
1989/01/07:昭和64.1.7
1989/01/08:平成元.1.8
2019/04/30:平成31.4.30
2019/05/01:元号元.5.1

11.0.2
1989/01/07:昭和64.1.7
1989/01/08:平成元.1.8
2019/04/30:平成31.4.30
2019/05/01:元号元.5.1

11.0.3
1989/01/07:昭和64.1.7
1989/01/08:平成元.1.8
2019/04/30:平成31.4.30
2019/05/01:令和元.5.1

Java 12

12
1989/01/07:昭和64.1.7
1989/01/08:平成元.1.8
2019/04/30:平成31.4.30
2019/05/01:元号元.5.1

12.0.1
1989/01/07:昭和64.1.7
1989/01/08:平成元.1.8
2019/04/30:平成31.4.30
2019/05/01:令和元.5.1

1.6、1.7は令和に対応していない。
1.8は1.8.0_211から対応している。
11は11.0.3から対応している。11は最初から新元号になることはわかっていたようで元号元と出力されている。
12は12.0.1から対応している。12も最初から元号元の表記がある。

まとめ

DotNetでは和暦の対応が急遽変更になったそうですが、Javaはそのまま元号が令和になっただけで大きな変更はなさそう。
しかし、DotNetの対応で確かにそうだなあと思ったのは、まだ元号が変わったわけではないので、現在の出力は平成31.5.1でも良いような気がする。令和になるのは令和元.5.1以降でも。
ましてや元号元.5.1なんて出力されたら、もっと困ってしまう。Java11で和暦変換していた人はどうしていたのだろうか?

同じタグの記事
同じカテゴリの記事

もうすぐ新元号

もうすぐ新しい元号になる。Windowsは新元号になったら、Windowsアップデートで新元号に対応するらしい。
いつも使っているJavaはどうなのだろう。そもそもJavaで元号変換できるのだろうか。

SimpleDateFormat

Javaの日付フォーマットでおなじみSimpleDateFormatで元号表示できた。

import java.util.Date;
import java.util.Locale;
import java.util.Calendar;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

var df = DateFormat.getDateInstance(DateFormat.SHORT, Locale.JAPAN);
var dateArray = new Date[] {
    df.parse("1989/01/06"),
    df.parse("1989/01/07"),
    df.parse("1989/01/08"),
    df.parse("1989/01/09"),
    df.parse("2019/04/29"),
    df.parse("2019/04/30"),
    df.parse("2019/05/01"),
    df.parse("2019/05/02"),
};

var dateFormatArray = new SimpleDateFormat[] {
    new SimpleDateFormat("GGGGy.M.d", new Locale("ja", "JP", "JP")),
    new SimpleDateFormat("GGGGyy.M.d", new Locale("ja", "JP", "JP")),
    new SimpleDateFormat("GGGGyyy.M.d", new Locale("ja", "JP", "JP")),
    new SimpleDateFormat("GGGGyyyy.M.d", new Locale("ja", "JP", "JP")),
};

for (var format : dateFormatArray) {
    for (var date : dateArray) {
        System.out.println(format.format(date));
    }
    System.out.println("--");
}
昭和64.1.6
昭和64.1.7
平成1.1.8
平成1.1.9
平成31.4.29
平成31.4.30
元号1.5.1
元号1.5.2
--
昭和64.1.6
昭和64.1.7
平成01.1.8
平成01.1.9
平成31.4.29
平成31.4.30
元号01.5.1
元号01.5.2
--
昭和064.1.6
昭和064.1.7
平成001.1.8
平成001.1.9
平成031.4.29
平成031.4.30
元号001.5.1
元号001.5.2
--
昭和64.1.6
昭和64.1.7
平成元.1.8
平成元.1.9
平成31.4.29
平成31.4.30
元号元.5.1
元号元.5.2
--

元号が表示できた。それもGGGGyyyyにすると、元年が出力される。
すばらしい。
java12で実行したが、未来の元号は「元号」と表示されるようだ。今しか見られないのだろうか。

まとめ

でも、社内システムで和暦を使うことは少ないと思う。
jshellを使って動かしてみたが、動作確認には便利だった。
java10以降使えるvarも意外と便利だし正しく動いた。

jdk1.8.0_201でコンパイルできるように修正して実行したところ、2019.5.1は平成31.5.1が返ってきた。

同じタグの記事
同じカテゴリの記事

Javaのバージョンチェック

Javaのバージョンチェック

テストツールの中でイメージファイルを作成している。
イメージファイルはTIFFファイル。Java9以降でないとTIFFファイルは作れない。
(作れなくはないけれど、それ用のライブラリが必要になる。)
イメージ作成前に、Javaのバージョンをチェックすることにする。
テスト実行しつもりで、TIFF作成に失敗して無意味な試験が実行されていたので。

バージョンチェック方法

1
2
3
4
5
6
String javaClassVersion = System.getProperty("java.class.version");
double javaClassVersionD = Double.parseDouble(javaClassVersion);
if (javaClassVersionD < 53) {
    System.out.println("Java9以降をお使い下さい。");
    System.exit(5);
}

53がJava9なので、それより小さい場合はエラーにしている。

手元のJava実行環境を見てみたが、こうなっているようだ。

java6 50
java7 51
java8 52
java11 55

System.getProperty(“java.version”)でもバージョンが取得できるが、見やすい形のバージョンが返ってきてしまうため、比較では扱いにくい。

java6 1.6.0_20
java7 1.7.0_80
java8 1.8.0_181
java11 11.0.1

まとめ

本当は、Javaバージョンに依存したつくりは好きではないが、まだJava8も捨てられないので、もう少しこのまま様子を見よう。

同じタグの記事
同じカテゴリの記事

Ruby公式サイトの他言語とRubyの違いを眺めてみて

Ruby公式サイトの他の言語とRubyの違いを眺めてみて

www.ruby-lang.orgの多言語からのRuby入門が面白かった。
他の言語と同じところ、違うところがまとめられている。
個人的にPythonとJavaが得意で、そこを眺めてみたが、こういう風にまとめられていると導入しやすいと思う。
気になったところを書き出してみる。

irbと言われる対話型の実行環境がある

この環境はとても便利。使ったことのないクラスを使う必要がある場合や、普段から使っているけどなかなか覚えられないsubstringの区切り位置を確認するときに重宝する。
文字列の2バイト目から5バイト欲しいときはどう指定するのか。対話画面があるとすぐに試せる。

今でこそ、Javaにもjshellができたが、それまでは小さなプログラムを作って、コンパイルして実行する必要があった。Eclipse 2の頃はスクラッチパットがあったが、今はどうなのだろう。
irbでいろいろ試しながら、スクリプトを作ることにしよう。

三重引用符で複数行リテラル

これは、Pythonでも好きなところ。Rubyでも使えるのは良いことだ。
Javaだったら、StringBuilder使うのも面倒なので、Propertiesにでも書き出しておくだろうか。

リストは[]、辞書は{}

PythonのTupleは無いようだ。あまり使わないから、どっちらでもよいが。

メソッド呼び出しの括弧はオプション

さすがPerlの次の言語だ。メソッドの呼び出しに括弧がいらない。Perlもそうであった。
昔のVBの様に戻り値の無いときの利用なら許容範囲かと思う。

True、Falseはtrueとfalse。Noneの変わりはnil。

PythonもTrue、Falseはtrueとfalseになってくれれば良いのに。
Noneとnilはnullになってしまえば良いのに。

importの代わりにrequireを使う

JRubyから始めたので、requireとimportの違いがいまだによくわからない。

MyClass.new()

インスタンスの作成が独特。new()もメソッドの扱いなのだろうか。
PythonならMyClass()。Javaならnew MyClass()。知ってしまえば、どれでも大した差はないか。

==とequals()

Rubyで文字列の比較は==。Javaならequals。
Rubyにはequalsは無く、equal?がある。しかしRubyのequal?はオブジェクトの比較用。Javaとは逆の動きだ。

まとめ

RubyもPythonもJavaも違いがあって面白い。
本当は何か1つの言語を極めるべきなのかもしれないけど、同じ言語ばかりだと飽きてしまうので、あれもこれも使っていきたいと思っている。

同じタグの記事
同じカテゴリの記事

javaでファイルの更新日時を変更する

javaでファイルの更新日時を変更する

日次バッチでファイルを作成している。
何の為かはわからないが、ファイルの更新日時をファイル作成日の0時0分0秒にしなくてはならない。
javaバッチなので、その流れでファイルの更新日時も変更する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
 * javac -encoding utf-8 TestFileTime.java
 *
 * java -cp . TestFileTime2
 */

import java.util.Date;
import java.nio.file.Path;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.text.SimpleDateFormat;
import java.io.Writer;

public class TestFileTime2 {
    public static void main(String[] args) {
        SimpleDateFormat ymd = new SimpleDateFormat("yyyyMMdd");
        String today = ymd.format(new Date());
        Path path = Paths.get(String.format("%s.txt", today));
        try (Writer w = Files.newBufferedWriter(path)) {
            w.write(today);
            w.write("\n");
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        try {
            FileTime fileTime = FileTime.fromMillis(ymd.parse(today).getTime());
            Files.setLastModifiedTime(path, fileTime);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

まとめ

ここまで作ったものの、javaバッチはshellで起動しているので、
shellでtouchすればよかった。

1
2
3
FILENAME=`date '+%Y%m%d.txt'`
TODAY=`date '+%m%d0000'`
touch -t $TODAY $FILENAME
同じタグの記事
同じカテゴリの記事

macにインストールしたjavaをアンインストールする

macにインストールしたjavaをアンインストールする

macを使っていて、気まぐれにjavaをインストールしている。
バージョンも覚えていない。複数のmacにインストールされたjavaのバージョンを
どれか決まったバージョンにしようと思った。
7だったり、8だったり、10だったり。
一旦削除したい。どうすれば良いのか?

下記の3つのディレクトリを順に削除する。

1
2
3
$ sudo rm -rf /Library/Internet Plug-Ins/JavaAppletPlugin.plugin
$ sudo rm -rf /Library/PreferencePanes/JavaControlPanel.prefPane
$ sudo rm -rf /Library/Java/JavaVirtualMachines/jdk1.8.0_*.jdk

ちなみにjdk-10の場合、3つ目のディレクトリは下記の通り

1
$ sudo rm -rf /Library/Java/JavaVirtualMachines/jdk-10.jdk

削除出来たかどうか確認

1
2
$ java
No Java runtime present, requesting install.

このように表示されれば、Javaがアンインストールされている。
最初、javaとかjdkとかが含まれるディレクトリを片っ端から削除しようと思っていたが、
macが管理している部分もあるようなので(インストールされていない時のメッセージ?)、余計なところは消してはいけないらしい。

さあ、mac達のjavaのバージョンは何にしようか・・・

同じタグの記事
同じカテゴリの記事

ちょっとしたフォーマッティング

ちょっとしたフォーマッティング

javaでログ出力をしていると、少しだけフォーマットしたいときがある。
テンプレートエンジンを利用する必要はない。
1行でパパっとフォーマッティングしたい。

例えば、処理件数を出力するとき、下記のように標準出力に処理中の件数をそのまま出力する。

1
2
3
4
5
6
7
8
9
long counter = 0;
for (Dto dto : dtoList) {
    logic(dto);
    counter++;
    if (counter % 1000 == 0) {
        System.out.println("処理中。" + counter);
    }
}
System.out.println("処理完了。" + counter);
1
2
3
4
5
6
7
8
9
10
11
12
13
処理中。1000
処理中。2000
処理中。3000
処理中。4000
処理中。5000
処理中。6000
処理中。7000
処理中。8000
処理中。9000
処理中。10000
処理中。11000
処理中。12000
処理完了。12345

たしかに、処理件数は出力される。
ここで、java.text.MessageFormatを利用すると、件数を3桁カンマ付きで出力できる。

1
2
3
4
5
6
7
8
9
10
11
import java.text.MessageFormat;

long counter = 0;
for (Dto dto : dtoList) {
    logic(dto);
    counter++;
    if (counter % 1000 == 0) {
        System.out.println(MessageFormat.format("処理中。{0}", counter));
    }
}
System.out.println(MessageFormat.format("処理完了。{0}", counter));
1
2
3
4
5
6
7
8
9
10
11
12
13
処理中。1,000
処理中。2,000
処理中。3,000
処理中。4,000
処理中。5,000
処理中。6,000
処理中。7,000
処理中。8,000
処理中。9,000
処理中。10,000
処理中。11,000
処理中。12,000
処理完了。12,345

String.formatを利用すると、出力桁を簡単に固定できるため、この方法も見通しが良い。

1
2
3
4
5
6
7
8
9
long counter = 0;
for (Dto dto : dtoList) {
    logic(dto);
    counter++;
    if (counter % 1000 == 0) {
        System.out.println(String.format("%7d 処理中。", counter));
    }
}
System.out.println(String.format("%7d 処理完了。", counter));
1
2
3
4
5
6
7
8
9
10
11
12
13
   1000 処理中。
   2000 処理中。
   3000 処理中。
   4000 処理中。
   5000 処理中。
   6000 処理中。
   7000 処理中。
   8000 処理中。
   9000 処理中。
  10000 処理中。
  11000 処理中。
  12000 処理中。
  12345 処理完了。

数字の桁数が合ったので、数字を前にした。

String.formatもMessageFormat.formatも、たくさんのフォーマットを持っているので、
すべてを覚えてもよいかもしれないが、やりたいことから覚えてもよいと思う。

同じタグの記事
同じカテゴリの記事

Kotlinを使ってみた

Kotlinを使ってみた

Javaを簡潔に記述できるKotlin。

KotlinでHello, World!下記のソースをHello.ktとして保存する。

1
2
3
fun main(args: Array<String>) {
    println("Hello, Kotlin!")
}

コンパイル、および実行。

1
2
3
$ kotlinc -include-runtime -d Hello.jar Hello.kt
$ java -jar Hello.jar
Hello, Kotlin!

javaコマンドで実行できてしまう。素晴らしい。

Kotlinの特徴

  • 行末のセミコロン不要。
  • javaクラスを自由に扱える。
  • コンパイルするとjavaクラスが作成される。
  • -dオプションで実行形式jarを作成することもできる。
  • 拡張子をktsにすれば、Kotlinスクリプトを記述でき、コンパイルなしに実行できる。
  • List、Mapが拡張され、lambda式で簡単にアクセスできる。

jythonの上を行きそう。

Kotlincでコンパイル

1
$ kotlinc -cp %CLASSPATH% -include-runtime -d Hello.jar Hello.kt

Hello.ktをコンパイルし、Hello.jarが作成される。
-include-runtimeを指定しているので、Kotlinのランタイムクラスがjarファイルに含まれる。
ランタイムは1MB程なので含めてよいと思う。
-include-runtimeを指定しなかった場合は、別途kotlin-runtime.jarを配布する必要がある。

kotlinc,kotlinコマンドの-cpは、jarの*指定はできなかった。それぞれのjarを個別に指定する必要がある。
例えば、Oracle.jdbcとCommonsLoggingを利用しているのであれば下記のように指定する必要がある。

1
2
$ CLASSPATH=/mylib/ojdbc6.jar;/mylib/commons-logging.jar
$ kotlinc -cp %CLASSPATH% -include-runtime -d Hello.jar Hello.kt

外部jarに依存しているのであれば、成果物のjarにランタイムを含めずに、上記の場合なら/mylibにKotlinのランタイムを追加すれば良いと思う。javaコマンドはクラスパスの*指定に対応しているので、下記のように実行できる。

1
$ java -cp /mylib/*:Hello.jar HelloKt

コンパイルしたクラスの実行

Kotlinコンパイル環境があるの場合

1
$ kotlin -cp %CLASSPATH%:Hello.jar HelloKt

HelloKtは実行クラス名。Hello.ktはHelloKtクラスとしてコンパイルされている。
kotlinコマンドでは、Kotlinのランタイムが自動的に参照されるので、コンパイル時にランタイムを含める必要はない。

javaの実行環境のみの場合

コンパイル時にkotlinランタイムを含めた場合

外部jarを参照していないのであれば

1
$ java -jar Hello.jar

外部jarを参照しているのであれば、

1
$ java -cp %CLASSPATH%:Hello.jar HelloKt

コンパイル時にkotlinランタイムを含めなかった場合

1
java -cp %CLASSPATH%:Hello.jar:kotlin-runtime.jar HelloKt

jaraの引数に-jarを指定すると、全てのクラスパスは無視されるので、
作成したjarもクラスパスに含めて、メインメソッドのあるクラスを指定する。
Hello.ktはHelloKtクラスとしてコンパイルされる。
Hello.ktにpackage指定されいている場合は、そのパッケージに所属した形でコンパイルされる。

下記のkotlinをHello2.ktとして保存。

1
2
3
4
package my.first
fun main(args: Array<String>) {
  print("Hello.")
}
1
2
3
$ kotlinc -include-runtime -d h.jar Hello2.kt
$ java -cp ./h.jar my.first.Hello2Kt
Hello.

Kotlinスクリプト

kotlinをコンパイルせずに、スクリプトとして実行できる。Kotlinスクリプトの拡張子はktsにしなければならない。

1
kotlinc -script Hello.kts

考察:jythonスクリプトをkotlinに置き換えるべきか

jythonもkotlinもJavaで実装されている。
Javaで実装されているため、Windows、Linux、macを問わず開発/実行できる。
Javaの豊富なライブラリを利用できる点はどちらも同じ。

では、jythonをkotlinに置き換えるべきなのだろうか。

jythonは、pythonのバージョンが2.7で止まっている。
python2.7なので、文字列の扱いがデリケートでる。
ここがとても煩わしい。jythonで色々なタイプの文字が出て来る場合は、
文字列結合も、jythonではなく、java.lang.StrinBuilderやjava.text.MessageFormatを使ったりしている。
kotlinでは、ここはあまり気にしなくて良さそうだ。

しかし、jythonの有利な点として、ファイルI/Oやディレクトリ操作、FTP、メール送信などがpythonとして簡単に記述することができる。
kotlinでも、色々kotlin拡張モジュールがあったり、強力なラムダ式を利用すれば、そこそこ簡単に実装できるようにも思えるが、pythonほどお手軽ではない。

pythonに慣れているために、こう考えてしまうのか。
もう少しkotlinでツールを作ってみよう。

同じタグの記事
同じカテゴリの記事

JavaでZip圧縮

JavaでZip圧縮

サーバ間のファイル連携で、XMLファイルを送信しているが、サイズが結構大きくなってしまう。
shellでZip圧縮してからFTP転送していたが、Javaから直接Zipファイルで出力する事にした。

JAXB.marshalの引数にOutputStreamを指定できるので、ZipOutputStreamを渡す。

1
2
3
4
5
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("out.zip")))) {
    ZipEntry ze = new ZipEntry("out.xml");
    zos.putNextEntry(ze);
    JAXB.marshal(dat, zos)
}

これで、out.zipの中に、out.xmlというファイル名で出力できる。
ディスクの遅いマシンで実行すると、Zipに出力した方がファイルサイズが小さくなり、出力処理が早くなった。
しかし、マシンによってはZip圧縮にコストがかかってしまい、処理が遅くなることもあった。

同じタグの記事
同じカテゴリの記事