Java
Java
Java8 がリリースされてから1年以上経過しているので Java プログラマの皆様はガシガシとその恩恵を受けた素晴らしいコードを書いていらっしゃるでしょう。
私は未熟者なのでボチボチとチマチマ楽しんでいます。(^_^;)
そこで問題です。
このプログラムを実行すると何が表示されますか?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
package jp.yucchi.hellorepeat; import java.util.Arrays; import java.util.List; public class HelloRepeat { public static void main(String[] args) { List<String> list = Arrays.asList("Hello", "World!"); for (int i = 0; i < list.size(); i++) { Runnable r = () -> System.out.println(list.get(i)); r.run(); } } } |
1. Hello
World!
2. World!
Hello
3. コンパイルエラー
4.実行時エラー
すいません。
簡単すぎますね。
答えは 3番のコンパイルエラーです。
ラムダ式の中から外部の変数を参照する場合、その変数はfinalもしくは実質的finalでなければならない。
このルールに違反しているからです。
どうすればこのプログラムを正しく動かせるでしょうか?
それも簡単なので答えを載せておきますね!
このプログラムは for ループ文を使っているのでまずは、拡張 for ループ文で修正してみましょう。
拡張 for ループ文は少しばかりの制約はあるけど要素を先頭から順に取得するような処理には便利に使えます。
for ループ文ではインデックス取得の為に実質的ファイナルでない変数にアクセスしてコンパイルエラーになりましたが拡張 for ループ文だとそのような心配はありません。
|
for (String str : list) { Runnable r = () -> System.out.println(str); r.run(); } |
あっけなく、簡単に修正できました。それでは、default void forEach(Consumer<? super T> action) を使って修正してみます。
|
list.forEach(e -> { Runnable r = () -> System.out.println(e); r.run(); } ); |
こちらの方法が Java8 っぽいので個人的には好きです。
でも内部的には、デフォルト実装の動作は次のようになっているそうです。
for (T t : this)
action.accept(t);
つまり、こういうことなのかな?
|
for (String str : list) { new Consumer<String>() { @Override public void accept(String e) { Runnable r = new Runnable() { @Override public void run() { System.out.println(e); } }; r.run(); } }.accept(str); } |
以上のことを考慮してラムダ式使う場合は、forEach(Consumer<? super T> action) を使うのが良さそうですね。
これで平凡なパズルはお終いです。
TAGS: Java |
2015年4月1日4:54 PM |
Java
エイプリルフール用のネタを用意できなかったので古典的な 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
|
package jp.yucchi.who; import java.util.function.Consumer; public class Who { private final String name; public Who(String name) { this.name = name; } private String name() { return name; } private void reName() { new Who("堀北真希") { Consumer<String> c = name -> { printName(name); }; private void printName(String name) { System.out.println("本名 " + name + " , 芸名 " + name()); } }.c.accept(name); } public static void main(String[] args) { new Who("原麻里奈").reName(); } } |
1.本名 原麻里奈 , 芸名 堀北真希
2.本名 原麻里奈 , 芸名 原麻里奈
3.コンパイルエラー
4.実行時エラー
解答は後日気が向けば書きます。(書くほどのもんじゃない(^_^;)
できれば面倒なので誰か種明かしをコメントとして書いてくれるとありがたいです。
TAGS: Java |
2015年4月1日5:48 AM |
Java
今日も簡単なアルゴリズムのエントリーです。
私が初めて覚えたソートアルゴリズムです。
当時はこれを考えた人はもの凄い頭のいい人だと感動しました。
バブルソート (Bubble Sort) は読んで字のごとく泡のソートです。(^_^;)
つまりソートの過程がデータが下から上へ移動する様子が、泡が浮かんでいくように見えることから名付けられたとされています。
最悪計算時間が O(n2) と遅いのですがシンプルで理解しやすい優しいアルゴリズムです。
そして安定であることが特徴の一つでもあります。
実際どんなことをしているかというと隣り合う要素の大きさを比較して並べ替えていくという単純なものです。
これを要素数 –1 回繰り返すことでソートを行なうことによって完了します。
バブルソートのアルゴリズムを解説したサイトは数多く存在します。
たいていの場合は「要素数-1回繰り返すことでソートを行う」を素直にコードにして紹介されています。
これはバブルソートの解説には解りやすくて良いのですが無駄が発生するので良くありません。
なので今回、私は涙ぐましい改良を施されたバブルソートを組みました。
ソートの部分的完了を検出し、比較範囲の限定を行っています。
ついでに配列が並び変わっていく様子を表示させてみました。(比較、交換作業は除く)
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
package jp.yucchi.bubblesort; import java.security.SecureRandom; import java.util.Arrays; /** * * @author Yucchi */ public class BubbleSort { 1 /** * @param args the command line arguments */ public static void main(String[] args) { int[] randomNumber = new SecureRandom().ints(10L, 1, 11).toArray(); System.out.println("befor:" + Arrays.toString(randomNumber) + System.getProperty("line.separator")); bubbleSort(randomNumber); System.out.println(System.getProperty("line.separator") + "after:" + Arrays.toString(randomNumber)); } private static void bubbleSort(int[] randomNumber) { int incompleteIndex = 0; // ソートがまだ完了していない位置 while (incompleteIndex < randomNumber.length - 1) { int lastChangeIndex = randomNumber.length - 1; // 最後に要素を交換した位置 for (int j = randomNumber.length - 1; j > incompleteIndex; j--) { if (randomNumber[j - 1] > randomNumber[j]) { // 要素の交換 本当は一時変数を用意して入れ替えるのが良い randomNumber[j - 1] ^= randomNumber[j]; randomNumber[j] ^= randomNumber[j - 1]; randomNumber[j - 1] ^= randomNumber[j]; lastChangeIndex = j; } } incompleteIndex = lastChangeIndex; trace(randomNumber); } } private static void trace(int[] randomNumber) { System.out.println("trace:" + Arrays.toString(randomNumber)); } } |
プログラムの実行結果は下図のようになります。
ザックリ説明すると例えば次のような配列が与えられた時
1, 2, 5, 8, 7, 9, 6
最初の while ループは
1, 2, 5, 8, 7, 9 ← 6
1, 2, 5, 8, 7 ← 6, 9
1, 2, 5, 8 ← 6, 7, 9
1, 2, 5 – 6, 8, 7, 9
1, 2 – 5, 6, 8, 7, 9
1 – 2, 5, 6, 8, 7, 9 (赤字は比較交換、緑字は比較未交換)
となり、配列のインデクスナンバー 3 まではソート完了となります。
1, 2, 5, 6, 8, 7, 9 (ピンク色の数字はソート完了済み)
よって、while ループの条件を可変することにより無駄な処理をへらせます。
また、内側の for ループも最後に要素交換した右側のインデックスナンバー(ソートが確定していない)を利用して
無駄に比較捜査をしないようにさせています。要するにソート済み部分の再捜査をしないことによって効率を上げようというものです。
次の while ループ処理は
1, 2, 5, 6, 8, 7 – 9
1, 2, 5, 6, 8 ← 7, 9
1, 2, 5, 6, 7, 8, 9
次の While ループで
1, 2, 5, 6, 7, 8 – 9
1, 2, 5, 6, 7, 8, 9
ソート完了!
もし、涙ぐましい改良がされていなければ無駄な比較処理がたくさん行われることになったでしょう。
今回は一般的なバブルソートの改良版を紹介しましたがバブルソートの亜種はまだあります。
シェーカーソートはバブルソートの亜種で安定のまま高効率化が達成?されています。
これはバブルソートを先頭からと末尾から交互に捜査して並べ替えていくものです。
だからシェーカーなんですね。(^_^)
アルゴリズムの基本的な考え方はバブルソートそのものなので興味のあるかたはプログラムを組んで楽しんでください。(^_^)
TAGS: Java |
2015年2月6日2:15 PM |
Java
ブログエントリーさぼらないために比較的よく知られているシンプルなアルゴリズムを書いてみた。
単純挿入ソート (Shuttle Sort) を書いてみました。
特徴としてトランプのカードを順番に並び替えるようなアルゴリズムであり、安定であること。
効率はあまり良くなく、O(n^2) です。
具体的には i 番目の要素(0番目からじゃない) に着目して i –1 番目と比較してそれより小さければ後ろにずらして挿入する。
これを繰り返しているだけの単純なアルゴリズムです。
特別にトリッキーなことはしてなくて理解しやすいアルゴリズムですね。
プログラムにはソートされていく過程をトレースして出力させています。
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 35 36 37 38 39 40 41 42 43 44 45 46
|
package jp.yucchi.shuttlesort; import java.security.SecureRandom; import java.util.Arrays; import java.util.stream.IntStream; /** * * @author Yucchi */ public class ShuttleSort { /** * @param args the command line arguments */ public static void main(String[] args) { int[] randomNumber = new SecureRandom().ints(10L, 1, 11).toArray(); System.out.println("befor:" + Arrays.toString(randomNumber) + System.getProperty("line.separator")); insertionSort(randomNumber); System.out.println(System.getProperty("line.separator") + "after:" + Arrays.toString(randomNumber)); } private static void insertionSort(int[] randomNumber) { IntStream.range(1, randomNumber.length).forEach(i -> { int j; int temp = randomNumber[i]; for (j = i; j > 0 && randomNumber[j - 1] > temp; j--) { randomNumber[j] = randomNumber[j - 1]; } randomNumber[j] = temp; trace(randomNumber); }); } private static void trace(int[] randomNumber) { System.out.println("trace:" + Arrays.toString(randomNumber)); } } |
このプログラムの実行結果は下図のようになります。
ちなみに Java8 を使っているので for 文でいいところを IntStream を使ったりしてます。
比較、入れ替えの for 文のところを Stream API を使ってできないものかと考えてみたのですが良いアイディアが浮かびませんでした。
配列の要素を並び替えるだけなので出来そうな気がしたのですが・・・(力不足、知恵不足、お小遣い不足)(ヲヒ
あっ、Stream API にはもちろん標準で sort の機能はあります。
だから本当はこんなアルゴリズム使う必要はありません!
そこんところ ヨロシク! by 永ちゃん
あと、乱数配列の生成は public IntStream ints(long streamSize,int randomNumberOrigin,int randomNumberBound) メソッドを使って生成しました。
超便利です!
今回もありきたりネタでした。(^_^)
TAGS: Java |
2015年2月5日7:07 AM |
Java
Blog エントリーさぼりがちになるのでしょうもないネタを書いときます。
Java 8 になって Stream API がめっちゃ便利に使えてよろこんでいる開発者たちがたくさんいますよね!
中には仕事で未だに古い Java を使わざるを得ない非常に可愛そうな環境のひとも・・・(ヲヒ
で、おそらく春に入社してくるフレッシュな新人プログラマには Java 8 の Stream API を使った新しい構文を勉強してもらうことになりますよね?
今さら、JDK 1.4, JDK 1.5, JDK1.6, JDK1.7 なんてことはないですよね。ねっ!ねっ!
そこで、1 から 1000 までの偶数だけの総和を求め。
2 の 0 乗から 2 の 32 乗までの総和を求める。
この二つを題材にして超シンプルなプログラムを Java 8 で組むとどうなるでしょうか?
次のような感じで組んでしまったら、あなたは新人さんに影で static おじさん じゃない・・・ Stream おじさん と呼ばれることになるかもしれません。
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
|
package jp.yucchi.bad.summation; import java.util.stream.DoubleStream; import java.util.stream.IntStream; /** * * @author Yucchi */ public class BadSummation { public static void main(String[] args) { // 1 から 1000 までの偶数だけの総和を求める。 int sum = IntStream.rangeClosed(1, 1_000) .filter(e -> (e & 1) == 0) .sum(); System.out.println("1 から 1000 までの偶数だけの総和は、" + sum + " です。"); // 2 の 0 乗から 2 の 32 乗までの総和を求める。 double powerSum = DoubleStream.iterate(0.0, i -> i + 1.0) .limit(32 + 1) .map(i -> Math.pow(2.0, i)) .sum(); System.out.println("2 の 0 乗から 2 の 32 乗までの総和は、" + powerSum + " です。"); } } |
えっ?
こんな感じのプログラムしか思いうかばないって!
Stream おじさん に認定します!
このプログラムは Stream API の説明サンプルには問題ないのですが効率が悪いのです。
まじめに勉強をしてきた新人さんほどこういったところを突いてくると思うので先に古典的な手法で求める方法を見せといてから
無理に Stream API を使うとこうなるよって言っとくのが無難だと思います。
新人プログラマの多くは次のようなプログラムを当たり前のように組むでしょう。
Stream API なんて使わずに古典的な手法を用いてシンプルに!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
package jp.yucchi.summation; /** * * @author Yucchi */ public class Summation { public static void main(String[] args) { // 1 から 1000 までの偶数だけの総和を求める。 int sum = 1_000 / 2 * (1_000 / 2 + 1); System.out.println("1 から 1000 までの偶数だけの総和は、" + sum + " です。"); // 2 の 0 乗から 2 の 32 乗までの総和を求める。 double powerSum = Math.pow(2.0, 32.0 + 1.0) - 1.0; System.out.println("2 の 0 乗から 2 の 32 乗までの総和は、" + powerSum + " です。"); } } |
いつの時代になってもこういった定番の処理方法は不滅です!
ちなみに偶数の総和は「日経ソフトウェア3月号」の「アルゴリズム&テクニック」で紹介されてます。
今月は他にも面白ネタが紹介されていておもしろかった。
この総和を求めるアルゴリズムは古典的な手法で広く知られているようで
「アルゴリズムパズル プログラマのための数学パズル」という本でもちょこっとだけ紹介されていました。
たとえ、カビが生えているような技術でもいいものはいつまでも使われるってことですね! (^_^)
以上、しょうもないネタでした。
TAGS: Java |
2015年1月31日5:38 AM |
« 古い記事
新しい記事 »