拡張for文の真の実力を知り、反復処理を使いこなせ【改訂版】Eclipseではじめるプログラミング(20)(2/3 ページ)

» 2010年12月21日 00時00分 公開
[小山博史株式会社ガリレオ]

java.lang.Iterableインターフェイスの独自実装

 さて、java.lang.Iterableインターフェイスを実装するクラスの使い方は分かりましたが、場合によっては、Iterableを実装するクラスを自作することもあるでしょう。このインターフェイスを実装したクラスSample05クラスを作ってみましょう。

 まず、「java.util.Iterator」インターフェイスを実装するクラスとしてSampleIteratorクラスを「staticな内部クラス」として用意しています。簡単に実装するため、java.util.ArrayListを使っています。このクラスは決まった文字列を要素として持つjava.util.Iteratorオブジェクトをメンバとして持ち、removeメソッドでは何もしません。

package sample20;
 
public class Sample05 {
    static class SampleIterator implements java.util.Iterator<String> {
        private java.util.Iterator<String> it;
        public SampleIterator() {
            java.util.ArrayList<String> list = new java.util.ArrayList<String>();
            list.add("第1回");
            list.add("第2回");
            list.add("第3回");
            list.add("第4回");
            list.add("第5回");
            it = list.iterator();
        }
        public boolean hasNext() {
            return it.hasNext();
        }
        public String next() {
            return it.next();
        }
        public void remove() {
            /* 何もしない */
        }
    }
    static class SampleIterable implements Iterable<String> {
        public java.util.Iterator<String> iterator() {
            return new SampleIterator();
        }
    }
  
    public static void main(String[] args) {
        SampleIterable iterable = new SampleIterable();
        for (String element : iterable) {
            System.out.println(element);
        }
    }
}
sample20/Sample05.java

 java.lang.Iterableインターフェイスを実装するSampleIterableクラスもstaticな内部クラスとして作成しています。このクラスはiteratorメソッドを実装するだけです。iteratorメソッドでは、単にSampleIteratorクラスのインスタンスを生成してその参照を返しています。

 Sample05クラスには、各staticクラス以外に、実行を確認するためのmainメソッドも定義してあります。そこでは、拡張for文を使ってSampleIteratorの各要素へアクセスしています。

continueとbreakで反復処理を上手く操れ!

 反復処理では、continueとbreakというキーワードを使うことにより、必要のない処理を飛ばして効率良く処理を進められます。

 continueを使うことにより、反復処理は継続させつつ、continue以降の処理をスキップして反復処理の本体の終わりまで処理を進められます。

 例としては、次のような処理があります。elementの値と「"第2回"」という文字列が等しい場合には、elementの値を出力せずに、それ以外ではelementの値を出力する場合は、次のように書くことができます。

package sample20;
 
public class Sample06 {
    public static void main(String[] args) {
        java.util.ArrayList<String> setExpression = new java.util.ArrayList<String>();
        setExpression.add("第1回");
        setExpression.add("第2回");
        setExpression.add("第3回");
        setExpression.add("第4回");
        setExpression.add("第5回");
        for (String element : setExpression) {
            if ("第2回".equals(element)) {
                continue;
            }
            System.out.println(element);
        }
    }
}
sample20/Sample06.java
第1回
第3回
第4回
第5回
実行結果

 breakを使うことにより、反復処理の途中で処理を終了できます。例として、elementの値が「"第3回"」という文字列と等しい時点で、反復処理を終了したいとします。そういった場合には、次のように書くことができます。

package sample20;
 
public class Sample07 {
    public static void main(String[] args) {
        java.util.ArrayList<String> setExpression = new java.util.ArrayList<String>();
        setExpression.add("第1回");
        setExpression.add("第2回");
        setExpression.add("第3回");
        setExpression.add("第4回");
        setExpression.add("第5回");
        for (String element : setExpression) {
            if ("第3回".equals(element)) {
                break;
            }
            System.out.println(element);
        }
    }
}
sample20/Sample07.java
第1回
第2回
実行結果

 continueは反復処理で使います。breakは反復処理とswitch文内で使います。どちらもよく使われるキーワードなので、覚えておくようにしましょう。

“入れ子”となった反復処理を使いこなせば、一人前

 入れ子となった反復処理をプログラムで実装したいことがあります。例えば、画像処理の分野では、各画素に対してアクセスするために使います。JavaでJPEGファイルを読み込んで、各画素の色をHTMLで出力してみるプログラムを作成してみましょう。

 Javaでは、ある方法で画像データをint型の配列へ変換して取得できます。取得した配列(ここでは、変数rgbArrayで表すとします)には、各点のRGB値が入っているので、それを使うことで色を取得できます。

 画像ファイルから変数rgbArrayを取得する具体的なコードは、以下のSample08.javaです。あらかじめ画像データを格納するための配列を用意してから、javax.imageio.ImageIOクラスを使うと簡単に取得できます。

 ImageIOクラスを使って、BufferedImageオブジェクトを入手したら、画像の幅(width)、画像の高さ(height)を取得して、画像データを格納するための配列rgbArrayを用意したら、後はBufferedImageクラスのgetRGBメソッドを呼び出すだけです。「sample.png」ファイルはプログラムを実行するフォルダへ置いてください。

 Eclipseでは、設定によってプログラムを実行するフォルダを変更できます。初期設定のままなら、プロジェクトの直下になります。その場合は、「src」フォルダがあるフォルダへ置いてください。

package sample20;
 
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
 
import javax.imageio.ImageIO;
 
public class Sample08 {
    public static void main(String[] args) throws Exception{
        String fileName = "sample.png";
        File imageFile = new File(fileName);
        BufferedImage image = ImageIO.read(imageFile);
  
        int width = image.getWidth();
        int height = image.getHeight();
        int[] rgbArray = new int[width * height];
        int offset = 0;
        int scansize = width;
        image.getRGB(0, 0, width, height, rgbArray, offset, scansize);
    
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int pixel = rgbArray[y * width + x];
                Color c = new Color(pixel);
                int r = c.getRed();
                int g = c.getGreen();
                int b = c.getBlue();
                System.out.print("("+x+","+y+")="+pixel+" ");
            }
            System.out.println("");
        }
    }
}
sample20/Sample08.java

 このようにすると、rgbArrayには幅width、高さheightの画像データが入ります。そこで、すべてのx座標、y座標の点について、変数rへ赤色の情報、変数gへ緑色の情報、変数bへ青色の情報を取得する処理と、各座標のpixel値を出力する処理を記述しています。

 ここで、ある画像データについて、「全体が暗く写ってしまっている画像データ」かどうかを自動判定したいとします。そういったときは、「画像データの中の黒い点の数が多過ぎる場合はエラーとする」といった処理をしたくなるはずです。エラーだということが分かった時点で処理を終了したいのですが、その場合はどうすればいいでしょう。

 ここで、画素の色が黒のとき、pixelの値が-16777216となるような画像データを対象としているとします。また、黒い点の許容範囲の“しきい値”を64000とします。すると、黒い点を数える変数blackCountを用意して、次のようにして判定できます。for文のところのみ抜粋します。

    int blackCount = 0;
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            int pixel = rgbArray[y * width + x];
            if (pixcel == -16777216) {
                blackCount++;
            }
            if (blackCount == 64000) {
                System.out.println("break x");
                break; // loop x
            }
            Color c = new Color(pixel);
                int r = c.getRed();
                int g = c.getGreen();
                int b = c.getBlue();
            System.out.print("("+x+","+y+")="+pixel+" ");
        }
        if (blackCount == 64000) {
            System.out.println("break y");
            break; // loop y break
        }
        System.out.println("");
    }
sample20/Sample09.java 抜粋

 「// loop x」とコメントが付いたbreak文で内側の反復処理を終了し、「// loop y」とコメントが付いたbreak文で外側の反復処理を終了しています。このことから分かるように、単に「break;」と書いた場合は、一番内側の反復処理を終了するだけです。

 次ページでは、入れ子の反復処理から一気に抜け出す“ラベル”の使い方を説明します。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。