Javaでrange

Javaでtimesのつづき。


前回はJavaRubyのtimesっぽいものを作って++を隠蔽したけど、まぁお世辞にも使いやすいとは言いがたい。一番の問題は、回数数えるだけなのに記述量が多いことだと思う。
ということで、今回は記述量もそれなりになるように、PythonのrangeをJavaで真似してみる。


負の数を指定したときとかの動作は迷ったけど、まぁ結構自然な動作かな。

public final class RangeUtils {

    private static final int[] EMPTY = new int[0];

    private RangeUtils() {}
    
    public static int[] rangeBySize(int size, int step) {
        int[] result = new int[size];
        for (int i = 0; i < size; i++)
            result[i] = i * step;
        return result;
    }
    
    public static int[] rangeBySize(int size) {
        return rangeBySize(size, 1);
    }
    
    public static int[] rangeUntil(int end, int step) {
        if (end == 0)
            return new int[] { 0 };
        if (step == 0)
            throw new IllegalArgumentException();
        
        int diff = (step < 0 ? -step : step);
        int size = (end < 0 ? -end : end) / diff + 1;
        
        int[] result = new int[size];
        for (int i = 0; i < size; i++)
            result[i] = i * (end < 0 ? -diff : diff);
        return result;
    }
    
    public static int[] rangeUntil(int end) {
        return rangeUntil(end, 1);
    }
    
    public static int[] rangeBetween(int start, int end) {
        if (start == end)
            return EMPTY;
        
        int size = (start < end ? end - start : start - end) + 1;
        
        int[] result = new int[size];
        for (int i = 0; i < size; i++)
            result[i] = i + start;
        return result;
    }
    
    public static int[] allSameValue(int size, int value) {
        int[] result = new int[size];
        for (int i = 0; i < size; i++)
            result[i] = value;
        return result;
    }
}

で、使うときはstaticインポートして、

// 0 1 2 3 4 5 6 7 8 9 
for (int i : rangeBySize(10))
    System.out.print(i + " ");
System.out.println();

// 0 1 2 3 4 5 6 7 8 9 10 
for (int i : rangeUntil(10))
    System.out.print(i + " ");
System.out.println();

// 1 2 3 4 5 6 7 8 9 10 
for (int i : rangeBetween(1, 10))
    System.out.print(i + " ");
System.out.println();

な感じ。


rangeBySizeは第一引数で指定したサイズのint型配列を生成する。
第二引数がなければ1ずつ、あれば第二引数に指定した数ずつ0から増やしていく。第一引数に負の数は指定できないが、第二引数には指定でき、その際の動作は0から減っていく。


rangeUntilは、第一引数に指定した数までのint型配列を生成する。
第二引数がなければ1ずつ、あれば第二引数に指定した数ずつ0から増やしていく。rangeBySizeと違い、第一引数に負の数を指定することもできる。第一引数に負の数を指定した場合、0から減っていく配列が生成される。
第二引数には0以外の数が指定できる。ただし、負の数は正の数が渡されたものとして動作する*1
また、rangeBySizeは第一引数に指定したサイズの配列が生成されるが、rangeUntilは第一引数を含む0からの配列が生成されるため、第一引数はサイズを表さない。


rangeBetweenは初期数値が設定できるrangeUntilのような動作である。基本的には第一引数以上第二引数以下の配列を生成する。
ただし、第二引数よりも第一引数の方が大きい場合は、第一引数以下第二引数以上の配列を生成する。


これによって、1オリジンのAPI*2を扱うときも、

for (int i : rangeBetween(1, size)) {
    ...
}

のように記述できる。timesよりもはるかに実用的かも。

*1:この辺の動作が微妙か・・・?

*2:JDBCとか