2015/02/27

JJUG ナイトセミナ 「至極の Java Quiz 傑作選」

このエントリーをはてなブックマークに追加

2 月 25 日に JJUG ナイトセミナ に寺田さん、宮川さんと一緒に登壇しました。ナイトセミナに登壇するのは半年ぶりです。

ちょうど日経ソフトウエアで連載していた至極の Java クイズが終了したこともあり、傑作選を行いました。傑作選とはいいつつも、そのままをクイズに出したら連載を読んでいる方が有利になってしまうので、すべてアレンジしてあります。また、1/3 は新作です。

資料はこちら。

 

先におわびをしておきます。

最近、オラクルの会場のプロジェクタが変わったのです。前回、オラクルで話した時に資料を 1024×768 で作成したのですが、その時に横に黒帯が出力されていたので気がついたのですが。

そこで、プロジェクタの解像度を問い合わせたところ、機種は Panasonic PT-DZ770S で、解像度が 1920×1200 だということでした。いわゆる HD ということです。

それを盲目的に信じた私が行けなかったのですが、会場に着いてみたら、いわゆるアナログ RGB のケーブルしかありません。そういえば、以前から HDMI はなかったことに気がついていればよかったのですが....

結局、1920×1200 は出力できず、可能なのは 1360×768 まで!!

PowerPoint を使っている人には全然問題ないかもしれませんが、JavaFX で資料を作っているこちらには大問題。キクタローさんやいがぴょんさんが講演している間に、なんとか改造して出力できるようになりました。

でも、当初の 0.63 倍にスケールダウンしているため、当初思っていたよりもフォントが小さく表示されてしまいました。ほんと見づらくて、すみませんでした。

でも、Swing で作っていたら改造はムリだったろうなぁ... ほんと、JavaFX でよかった。

 

そんなこんなで、かなり焦った状況でセッションを始めてしまったので、始めはグダグダでしたね。本当は、Java Puzzlers って何なのか、なぜ寺田さんと私は青いつなぎを着ているのか、日経ソフトの Java クイズとは、などを説明する予定だったのですが...

寺田さんが、そこらへん補ってくれればよかったのですが、みんなで焦ってしまったので、まぁしかたありません。

ちなみに、Dukelele が 2 台というのは、本邦初だし、もうやらないだろうなと思います。

 

せかっくなので、セッションでははしょってしまったパズルの解説をここでやろうと思います。私が出題したのは、1, 4, 7 問目です。

 

#1 Vertigo

ちなみに Vertigo というのはめまいのことです。

問題はこちら。

import java.util.function.Supplier;

public class Vertigo {
    static class String {
        private Supplier direct;
        
        public String(java.lang.String director) {
            this.direct = () -> director;
        }
        
        public Supplier direct() {
            return direct;
        }
    }
    
    public static void main(String... args) {
        String alf = new String("Hitchcock");
        System.out.println(alf.direct().get());
    }
}

選択肢は

  1. Hitchcock
  2. 何も表示されない
  3. コンパイルエラー
  4. それ以外

答えは 4. のそれ以外です。

まず、気になると思うのが、String クラスを作れるのかどうかという点。

答えはできます。String クラスでも System クラスでもパッケージが異なれば作れます。

次に怪しいのが、main メソッドの String alf = new String("Hitchcock"); で使われている String クラスが、どの String クラスを指しているかという点。

これは、内部クラスの String クラスを指しています。

でも、それだけだと、1. の Hitchcock が出力されるのでは、と思いますよね。

もう 1 つ忘れてはいけない点が、main メソッドの引数の String クラスです。

この String クラスも内部クラスの String クラスを指しています。

でも、main メソッドの引数に使われるのは、java.lang.String クラスではないとダメですよね。なので、このプログラムを実行しようとすると、「メイン・メソッドが Vertigo で見つかりません」というエラーメッセージが表示されて、プログラムは動きません。

つまり、答えは 4. のそれ以外です。

さて、これをどうやれば修正できるかですが、一番単純なのは main メソッドを main(java.lang.String... args) のようにフルパッケージで指定することです。

しかし、これは本質的な答えではありません。

重要なのは内部クラスの String クラスは、その名前でいいのかどうかということです。

String クラスに direct メソッドなんて変な感じですよね。また、コンストラクタの引数名が director というのも。ここは String ではなくて、Movie というクラスにしてしまえば、しっくりきます。

つまり、クラス名はそのクラスの責務をちゃんと表す名前にする必要があるわけです。

ということで、この問題の教訓。

  • 標準ライブラリと同じ名前のクラス名は避ける
    • 特に java.lang パッケージのクラス名との重複は避ける
  • クラス名はとても重要
    • 責務に応じた名前にする

 

ところで、Vertigo ははじめに書いたように「めまい」のことなのですが、めまいと Hitchcock (アルフレッド ヒッチコック) といえば、映画の「めまい」ですよね。映画の「めまい」の中ではヒロインが 1 人 2 役をやっていて、主役の刑事を惑わせるというストーリーなんです。最後はそれがばれて、ヒロインの人は死んじゃうのですが...

ここでも、2 つの String クラスが出てきて、惑わせてくれるわけでした。

BGM もめまいのサントラを使いたかったのですが、さすがにそれは持っていないので、手持ちの CD を探したらありました。

U2 の Vertigo。

この曲、iPod の CM にも使われてましたね。

 

#4 Take Five

4 問目は Take Five です。

問題はこちら。

public class TakeFive {
    public static void main(String... args) {
        int beat = 0;
        long count = 0;
        
        for (; beat < Integer.MAX_VALUE; beat += 5) {
            count++;
        }
        
        if (beat == count * 5) {
            System.out.println("Break");
        } else {
            System.out.println("Keep Rhythm");
        }
        
        System.out.println(beat);
        System.out.println(count);
    }
}

選択肢は

  1. Break
  2. Keep Rhythm
  3. 例外発生
  4. それ以外

答えは 2. の Keep Rhythm です。

この問題はプリミティブ型の演算で避けることのできない、オーバーフローの問題です。

問題としては、for 文で beat を 5 ずつ増加させていき、それといっしょに count もインクリメントしています。for 文の終了条件は beat が Integer.MAX_VALUE 以上ということ。

beat は 0 から始まって、5、10 と値が増えていきます。普通に考えれば、beat == count * 5 は true ですよね。

難しいのは Integer.MAX_VALUE 付近の話。

Integer.MAX_VALUE の値は 2,147,483,647。16 進数で表すと、0x7FFF_FFFF です。ここにもっとも近い 5 の倍数は 2,147,483,645、16 進数だと 0x7FFF_FFFD です。

この値に 5 を足すと、2,147,483,650 にはなりません!

16 進数で考えると、5 を足した数は 0x8000_0002 になります。ここで思い出してほしいのはプリミティブ型を 2 進数で表した時の最上位ビットは符号を表していることです。

0x7FFF_FFFD の最上位ビットは 0 ですが、0x8000_0002 の最上位ビットは 1 になります。つまり、0x8000_0002 は負の数を表していることになります。10 進数であらわすと、-2,147,483,646 になってしまいます。

これが桁あふれ、つまりオーバーフロー、正確には整数オーバーフローです。

オーバーフローして負の数になってしまったのですから、Integer.MAX_VALUE より小さい値です。なので、まだ for 文は回り続けます。

次に、Integer.MAX_VALUE に再接近するのは beat が 2,147,483,644、そして 2,145,483 です。ここでまたオーバーフローして、ループを繰り返し、ちょうど beat が Integer.MAX_VALUE になるわけです。

この時の、count の値は 3,006,477,107。count は long 型なので、この値でも表すことができます。ちなみに、3,006,477,107 * 5 / Integer.MAX_VALUE は 7 になります。

Java は整数オーバーフローは検出できないので、こんなことになってしまうわけです。

では、どうやって直せばいいかというと、for 文の終了条件を beat < Integer.MAX_VALUE から beat < Integer.MAX_VALUE - 5 にすれば OK。これだと、5 を足しても MAX_VALUE を超えないので、オーバーフローにはなりません。

しかし、本質的に直すのであれば、変数の値の範囲を考えて beat 変数の型を long 型にする、もしくは BigInteger クラスにすることです。

この問題ではオーバーフローを扱いましたが、同様に小数点数におけるアンダーフローの問題もあります。どちらも Java では避けて通れないので、十分に気をつけましょう。

ということで、この問題の教訓。

  • オーバーフロー、アンダーフローに注意する
  • 変数の値の範囲から適切な型を選択する
  • 範囲が限定できないのであれば、BigDecimal/BigInteger の使用も検討する

 

ところで、この問題の Take Five ですが、英語の意味は 5 分休憩です。でも、やっぱり Dave Brubeck の Take Five なわけです。

この曲は 5 拍子。なので、5 で割り切れるところで Break、その他で Keep Rhythm としてます。

5 拍子とはいっても、実際には 3 拍子 + 2 拍子的なリズムですね。だから変拍子にも関わらず、聞いていると心地よいのかもしれません。

 

#7 Ants and Grasshopper

最後の問題は Ants and Grasshopper です。イソップのアリとキリギリスは Ant and Grasshopper ですが、ここでは Ant ではなくて Ants です。

この問題は普通のシーケンシャルなストリームとパラレルストリームのベンチマークです。

セッションでは説明したのですが、こういうベンチマークで 1 回だけの試行で性能を云々することはしません。通常は事前にウォーミングアップした後に、複数回の試行を行うようにします。

ということで、セッションで示したものを書き直したのが、次のプログラムです。

import java.util.Random;

public class AntsAndGrasshopper {

    static long grasshopperWork() {
        return new Random().longs(1_000_000L)
                           .sum();
    }

    static long antsWork() {
        return new Random().longs(1_000_000L)
                           .parallel()
                           .sum();
    }

    public static void main(String... args) {
        // ウォーミングアップ
        for (int i = 0; i < 50; i++) {
            grasshopperWork();
        }

        long s = System.nanoTime();
        for (int i = 0; i < 50; i++) {
            grasshopperWork();
        }
        long ghTime = System.nanoTime() - s;

        // ウォーミングアップ
        for (int i = 0; i < 50; i++) {
            antsWork();
        }

        s = System.nanoTime();
        for (int i = 0; i < 50; i++) {
            antsWork();
        }
        long atTime = System.nanoTime() - s;

        System.out.println("Ants work faster: " + ((ghTime - atTime) > 0));
    }
}

選択肢は

  1. Ants work faster: true
  2. Ants work faster: false
  3. 場合による
  4. それ以外

この問題はもちろん、マルチコアの CPU で実行することを想定しています。

grasshopperWork メソッドと ants メソッドで使用されている、Random クラスの longs メソッドですが、引数で与えた個数の long 型の乱数列の LongStream オブジェクトを生成するメソッドです。Random クラスには longs メソッド以外にも、ints メソッド、doubles メソッドが追加されています。

この乱数列の合計を計算するのが、grasshopperWork メソッドと antsWork メソッドのわけです。両者の違いは grasshopperWork メソッドがシーケンシャルなストリームを使用し、antsWork メソッドがパラレルストリームを使用しているところです。

ここでは、ウォーミングアップに 50 回それぞれのメソッドをコールし、その後、再び 50 回コールした合計の時間で比較しています。

普通だったら、当然パラレルストリームが速いわけですから、1. の Ants work faster: true になるはずです。

ところが、実際には違います。

パラレルストリームの方が遅くなるのです。実際にやってみると、ビックリするぐらいパラレルストリームの方が遅くなりますよ。

この理由を考えてみましょう。もちろん、怪しいのは Random クラスですね。

Random クラスの中で、乱数を生成しているのは next メソッドです。

    protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
            oldseed = seed.get();
            nextseed = (oldseed * multiplier + addend) & mask;
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }

乱数列を生成するには、種 (seed) が必要なのはご存じでしょうか。デフォルトコンストラクタで Random オブジェクトを生成すると、時間を基に seed が生成されます。

この seed の型は AtomicLong クラスです。つまり、Random クラスはスレッドセーフで複数のスレッドからアクセスすることが可能です。

そして、seed から新しい nextseed を生成しています。そして、青字部分の compareAndSet メソッドで、seed が変更されていないかどうかをチェックし、変更されていなければ新しい nextseed に更新しています。

つまり、他のスレッドがアクセスして、seed を変更してしまうと、この while ループがずっと回り続けるわけです。

そして、残念なことにこのループは非常に多くの回数ループしてしまいます。

実をいうと、AtomicLong クラスは複数のスレッドから頻繁に値が更新されることを想定していません。頻繁に値が更新するような場合は、Java SE 8 で導入された LongAccumulator クラスを使用します。

LongAccumulator クラスの Javadoc には以下のようなことが書いてあります。

きめ細かい同期制御のためではなく統計収集などの目的に使用される共通の値が、複数のスレッドによって更新される場合、通常は AtomicLong よりもこのクラスをお薦めします。更新の競合が少ないときは、2つのクラスの特徴は似ています。競合が多いときは、期待されるスループットはこのクラスの方がかなり高くなります。ただし、容量消費も多くなります。

日本語が分かりにくいですが、ようするに更新を多くする場合は LongAccumulator クラスを使えということです。ただし、LongAccumulator クラスはヒープも多く喰います。

つまり、パラレルストリームのように複数のストリームでアクセスすると、頻繁に競合が発生しパフォーマンスが劣化してしまうわけです。

このようにパラレルストリームの処理中に、外部の変数にアクセスすると、とたんにパフォーマンスが劣化します。イミュタブルのオブジェクトであればまだしも、状態を保持しているオブジェクトはダメです。

シンクロナイズしていないオブジェクトはもちろんダメですが、シンクロナイズしていても競合が頻繁に発生するので、パフォーマンスは劣化してしまいます。

Random クラスだけを見ていると気がつかないかもしれませんが、Random クラスは状態 (種のことです) を保持していることを考えれば、パラレルストリームではパフォーマンスが劣化することが予想できます。

このようにパラレルストリームにはやってはいけないことがいろいろあります。代表的なものを以下に列挙します。

  1. ストリームの要素数が少ないのはだめ。少なくとも 106 は要素がないと、効果が出ない
  2. 外部変数へのアクセス
  3. 順序に依存した処理
  4. 無限ストリーム
  5. 入出力

まず、1. の要素数です。

パラレルストリームはパラレル処理を行うため、シーケンシャルに実行することよりもオーバーヘッドがあります。このため、要素数が少ないと、オーバーヘッドの分だけパフォーマンスが上がりません。

私が試したケースからいうと、Java SE 8 の実装だと、要素が 106 以上ないと効果が出ませんでした。もっと、コア数が多いような場合には異なるかもしれませんし、今後はもっと少ない要素で効果が出るような実装になるかもしれません。

少なくとも、パラレルストリームのオーバーヘッドは少なくはないということは注意した方がいいと思います。

次の 2. が今回のケースですね。実際には、ストリームに処理させるラムダ式の中から、外部にアクセスしてしまうというケースが多いと思います。

3. は当然ですね。せっかくパラレルで処理しているのですから、順序に依存してしまうと意味がなくなってしまいます。

4. の無限ストリームは意外に思われるかもしれません。

無限ストリームはストリームの長さが明示的に決まらないストリームのことで、ストリームを iterate メソッドや generate メソッドで生成した場合に相当します。

なぜ、無限ストリームはパラレルだとパフォーマンスが落ちるのかは、パラレルストリームの処理手法を理解する必要があります。

パラレルストリームの処理は、Java SE 7 で導入された Fork/Join Framwork を使用して行われています。

Fork/Join Framework はタスクを細かくするために、分割統治法を使用します。分割統治法は、すべてのタスクをまず半分に分割します。そして、半分に分割したタスクをそれぞれ半分に分割します。このように半分に分割することを繰り返して、タスクを細かい粒度にしていきます。

ストリームの場合、タスクを分割するのではなく、タスクで処理する要素を分割していきます。つまり、ストリームの要素を半分に分割していくのです。この分割は要素が 1 つになるまで繰り返され、1 つになったらタスク処理します (この場合、ラムダ式で記述された処理)。そして、それを再び組み合わせていって、最終的な結果を得ています。

ところが、無限ストリームの場合、要素数が分からないので、要素の半分が分かりません。つまり、どこで分割していいのかが分からなくなってしまうのです。このため、分割が非効率になり、結果的にパフォーマンスが劣化してしまうわけです。

最後の入出力は Files クラスの lines メソッドや BufferedReader クラスの lines クラスに相当します。ファイルの読み込みや通信は CPU の処理スピードに比べると、遥かに遅いです。I/O 待ちで CPU が遊んでしまったら、何のためのパラレル処理なのか分からなくなってしまいますね。

この辺りの議論は、 ITpro の 詳説 Java SE 8 第 16 回 に解説したので、こちらもご参照ください。

さて、この問題ですが、どのように直せばいいでしょうか。

問題は競合を起こしている部分なのですから、その処理はパラレルで行わず、シーケンシャルに行えばいいのです。つまり事前に乱数列をシーケンシャルに生成しておきます。

    static long antsWork() {
        // 事前に乱数列を生成しておく
        long[] rands = new Random().longs(1_000_000).toArray();
 
        return Arrays.stream(rands).parallel().sum();
    }

とはいえ、この方法だと乱数列の rands を生成する方に時間がかかってしまうので、パラレルストリームの性能を測るという意味ではあまり適切ではありません。

乱数列は別に生成しておいて、それをメソッドの引数にしておく方が適切ですね。

    static long antsWork(long[] rands) {
        return Arrays.stream(rands).parallel().sum();
    }

もちろん、同じように grasshopperWork メソッドも変更します。これで実行すると、antsWork メソッドの方が処理時間が短くなるはずです。

ということで、この問題の教訓

  • パラレルストリームは落とし穴も多い
  • 動作原理を理解して、パフォーマンスを向上させる
  • 関数を使った考え方を理解する

 

さて、この問題の BGM ですが、Dave Matthews Band の Ants Marching です。

Dave Matthews は結構 CD を持っているつもりだったのですが、この曲が入った Under the Table and Dreaming は持ってませんでした ><

しかたないので、この曲だけライブ盤を使用しました。ライブの拍手が入っていると、そこで拍手強制しているみたいでイヤなのですが、しかたありません。まぁ、それに気づいた人はいなかったようでしたけど。


他の問題は、出題してくれた本人が解説してくれるでしょう。してなかったら、頃合いを見て、解説したいと思います。

なので、使用した BGM だけ。

2 問目は TriTrial。

問題では試行の意味の Trial なのですが、試練の意味の Trial をかけて、Peter, Paul and Mary の All My Trial を使いました。

3 問目は Answer to Everything。

この問題のタイトルは銀河ヒッチハイクガイドに引っかけてあります。問題の中にも 42 が出てきますし。ということで、映画の銀河ヒッチハイクガイドで使われていた、So Long and Thanks for All the Fish を使っています。この曲、映画では一番始めに使われるのですが、とても印象的でしたね。

5 問目は Kana Catalog。

この問題の BGM が一番困ったのでした。Catalog を扱った曲なんか持っていないし...

しかたないので、Catalog といえば Book だろうということで、Miles Davis の I Could Write a Book です。

この曲は Miles のマラソンアルバムの Relaxin' に入っているのですが、櫻庭はこの頃の Miles が一番好きなわけです。

6 問目は Odds and Sods。

この曲は外してしまいました ><

Odds and Sods の意味は「がらくた」です。だとしたら、グランジロックだろうということで、グランジの代表曲といってもいい、Nirvana の Smells Like Team Spirit にしたわけです。

ところが、当日判明したのが、このタイトル The Who のアルバムが由来だったのです。確かに宮川さんは 60 年代のブリティッシュロックが好きだったなぁ。

でも、さすがにこのアルバムは持ってませんでした ><

8 問目と 9 問目はセッションでは使わなかったのですが、使用する予定だったの曲を紹介しておきます。

8 問目は Superstar。

問題を見れば分かると思うのですが、Superstar といっているのはあの人のことです。ということで、ゴスペルの名曲、John The Revelator を使いました。

John The Revelator はいろいろな人が歌っていますが、ここで用意したのは、映画の Blues Brothers 2000 の Sam Moore のバージョン。Sam Moore といえば、Sam & Dave の Sam。パワフルです。

9 問目は Metamorphoses

最後は Metamorphose といえばやっぱり蝶でしょう、ということで Alicia Keys の Butterflyz です。これはとってもキレイな曲。Alicia のピアノがいいですね。


なぜか、よく分からないのですが、この Java Quiz とても反響が大きく、ビックリしています。

はてブのホットエントリ入りしていたり、Slideshare の view 数がかつてないほど急激に増えていたりで、ほんとビックリです。

問題を作るのはとっても大変なのですが、もし需要があれば、また寺田さんと一緒に企画してみようと思います。

2015/02/06

富山合同勉強会 .NET & Java

このエントリーをはてなブックマークに追加

1 月 30 日に富山で開催された .NET と Java の合同勉強会 に参加してきました。

毎冬、食べもの駆動で富山にいってますが、.NET の勉強会と合同なのは初めてです。同じ日に、同じ建物でやっているということはあったんですけどね。

しかも、今回は合宿形式で温泉!

でも、櫻庭はそれを気づかずに、富山市内のホテルを予約してしまったのでした ><

参加人数は .NET : Java = 3 : 1 ぐらい。もっと Java 勢にがんばってもらわないと! でも、このぐらいの人数だと、参加者との距離が近いのでツッコミもしやすいし、議論できたりもするのでいいですね。

さて、櫻庭は Project Valhalla と Project Panama の話をしてきました。資料はこちら。

 

 

Project Valhalla と Project Panama は Java SE 10 に入るかもしれない機能です。両方とも VM レベルのパフォーマンス向上などを目的にしたプロジェクトです。

でも、なんでそういう機能が必要なのかというところをまず話しました。

CPU のパフォーマンスが向上しない壁があって、電力 (熱)、ILP (Instruction level parallelism)、メモリアクセス、光速 (配線長) の 4 つです。

特に、メモリアクセスがネックになっていることが多くあります。CPU のパフォーマンス向上に比べると、メモリアクセスのスピードの伸びは低いので、スピードのギャップがどんどん広がっています。

かつて、Brian Goetz が JavaOne のセッションで Memory is the New Disk と言っていたのが、櫻庭にはとても衝撃的でした。

じゃあ、遅いメモリアクセスをしないようにするにはどうするのかというと、キャッシュメモリです。今の CPU はだいたい L1, L2, L3 の 3 段階のキャッシュメモリが備わっています。

L1 へのアクセスは 3 クロックぐらい、L2 が 15 クロックぐらいだそうです。それに対してメインメモリへのアクセスは 200 クロック程度。以下に遅いかが分かります。

つまり、メインメモリにアクセスする状況をなるべく減らすことがキモになります。具体的にはキャッシュミスを如何に減らすかということです。

では、Java ではどうなのという答えの 1 つが、Project Valhalla と Project Panama になるわけです。

Project Valhalla は主に以下の 3 つの仕様について議論されています。

  • ValueType
  • Specialization
  • VarHandle

まず、ValueType ですが、これはプリミティブ型以上、レファレンス型未満とでもいえばいいような型です。C の構造型のようなものとでもいえばいいかもしれません。しかも、イミュタブルに限定されます。

で、ValueType を使う利点は何かというと... たとえば ValueType の配列を考えてみましょう。リファレンス型だと配列にはリファレンスが保持され、そ参照先のオブジェクトはヒープのあちこちに散らばっています。

配列といえば、まとめて扱うことが多いわけですからなるべくまとめてキャッシュにおいておきたいわけですが、オブジェクトがヒープに散らばっているとまとめてキャッシュにおくということが難しくなります。

で、ValueType を使用すると、配列の中に ValueType のフィールドが展開されます。なので、キャッシュにのせやすいわけです。

ValueType の使用法としては、複素数であるとか、long より大きい数値を扱う場合、固定小数点数、タプルなどいろいろあると思います。特にタプルが実現できそうなのはいいですね。

このように ValueType を使用するとプリミティブと同じようにオブジェクトを扱うことができます。たとえば、OptionalInt クラスなんて ValueType で置き換えられますよね。

でも、OptionalInt クラスなんてほんとは使いたくなくて、Optional クラスで十分だと思いませんか。なんでこんなクラスがあるかというと、ジェネリクスの型パラメータとしてプリミティブ型を指定できないからです。

いろいろと過去の経緯や、互換性を重視して今のようにプリミティブ型を使えなくなっています。

これに対して、プリミティブ型を型パラメータとして使えるようにしようというのが、Specialization です。

今のジェネリクスの実装方法はイレージャ方式と呼ばれていますが、これとは違う方法でプリミティブ型を扱えるようにするということです。

これを使えば、IntStream だとか、IntFunction だとか、OptionalInt なんてインタフェース/クラス群は黒歴史として闇に葬り去られそうですw プリミティブ型のラッパークラスもほとんど必要なくなりますね。

まぁ、Specialization にはいろいろと問題も山積みなので、実現するのは大変だと思いますけど。

ちなみに、セッションではほとんど触れなかったのですが、VarHandle は volatile を拡張するためのものです。

 

さて、次の Project Panama です。Project Panama も 3 つの仕様が議論されています。

  • Array 2.0
  • JNR/FFI
  • Data Layout Control

Data Layout Control に関してはあまり公開されている資料がなくて、話せることがないのですが....

Array 2.0 はその名の通り配列をバージョンアップしましょうという仕様です。その中には多次元配列も含まれています。今の Java の 2 次元配列は正確には 2 次元配列ではなくて、単に配列の配列になっています。でも、これだとやっぱりキャッシュにのりにくいんですよね。

それに対して Panama が導入されれば、ちゃんとした 2 次元配列を使うことができるようになります。2 次元配列使いまくりの画像処理なんかはすごい恩恵があるはず。後は、行列計算とか。

JNR はネイティブコードを呼び出すための API です。ネイティブコードを呼び出すには JNI を使ってますけど、これがまた使いにくいし、パフォーマンスもよくない。JNA というのもありますけど、JNA は使いやすいけど、パフォーマンスはイマイチ。

ということで、JRuby の Charles Nutter が中心になって進めているのが JNR (Java Native Runtime) です。

JNR の基板となっているのが、JFFI です。FFI は Foreign Function Interface のことで、一般的な言葉。Java 版の FFI ということで JFFI です。

JNR は JNI と同じようにネイティブコードをコールするだけではなく、Posix 準拠の API を提供していたり、UNIX Socket をそのまま使えたりと、多機能。しかも、パフォーマンスもいいのです。

JNR はもう実装も結構進んでいるので、もしかしたら JNR だけ取り出して Java SE 9 に入れるということもあるかもしれません。

 

ということで、ちょっと未来の Java の話をしてきたわけなのですが、ここらへんの問題って .NET/C# だともう織り込み済みだったりするんですよね。Java が停滞していた時に、C# などはどんどん進化していたので、がんばって Java も追いつかないと。

最後に、参考資料を上げておきます。

2014/10/11

◆Java SE 8勉強会◆女子部もラムダ式に挑戦!

このエントリーをはてなブックマークに追加

Java 女子部のイベントで Project Lambda のセッションとハンズオンの先生役をやってきました。

事前に初心者が多いことを聞いていたので、ハンズオンの資料は以前のラムダ道場ではなくて、もうちょっと簡単なラムダお稽古を作成しました。資料は GitHub で公開しています。

 

Lambda Okeiko https://github.com/skrb/LambdaOkeiko

 

しかし、このハンズオン資料の作成に思いのほか時間がかかってしまって、セッション資料はほぼ使い回し。スイマセン ^ ^;;

それでも、セッション資料も 1/3 ぐらいは差し替えたんですけどね。とはいうものの、基本的には沖縄で使用したものと同じなので、こちらを貼っておきます。

 

 

さて、セッションとハンズオンの内容ですが、初心者向けということでかなり内容を絞りました。今回、扱ったのは

  • Lambda 式
    • Effectively final

  • Stream
    • Collection#stream
    • Arrays..stream
    • filter
    • map/mapToObj
    • flatMap
    • forEach
    • collect

Lambda 式は扱っても、インタフェースの default 実装や static メソッドは扱いませんでした。

また、メソッド参照もやりませんでした。メソッド参照はコンストラクタ参照などを含めると結構バリエーションあります。しかし、全部説明しても結局使うのは System.out::println ぐらいだし、逆に使いすぎると分けが分からなくなりがち。

厳密な Lambda 式と匿名クラスの違いなんかも触れていません。

Stream も 5 種類のメソッドだけで、reduce など他のメソッドはまったくやりませんでした。

でも、この 5 種類だけ使えれば、だいたい使えるし、Stream の基本的な使い方さえ分かれば他の補助的なメソッドも使えるはずと見込んだわけです。

でも、いろいろ想定と違っている部分も...

たとえば

  • 匿名クラスを使ったことない
  • NIO はもちろん、Thread も使ったことない
  • Iterator は使ったことあるけど、拡張 for 文は書いたことがない

デスクトップアプリケーションばかり作っている櫻庭にとって匿名クラスを使わないということはありえないのですが、サーバー系だと使わないんでしょうね。Android をやっている人も少なかったし。マルチスレッドを使ったことがないというのも、サーバー系だからなのでしょう。

拡張 for 文は新人を教えるためのテキストが古いままになっているせいではないかと教えてもらいました。新人教育用のテキストなんてそんなに書きかえるわけもないので、そういうものかもしれません。でも、それはかなりまずいと思うんだけどなぁ....

まぁ、それはそれとしてそこそこ楽しんでいただけたのではないでしょうか。

 

さて、Java 女子部お菓子係としては、お菓子も忘れてはなりません。今日のお菓子は

をご用意いたしました。

たぶん、ハンズオンよりも、こちらの方が満足度は高かったのではww