半角スペース区切りの自然数を取り出す方法を色々試した
こんにちは。「Javaを復習する初心者」です。自然数が半角スペースで区切られているという状況を考えました。区切られている自然数はint型の範囲に収まると仮定します。各自然数を取り出し、int型配列に格納するという方法を考えます。この方法をいくつか考え、速さを比較してみました。
分割するものは「12 345 678」というような、半角スペースで区切られた自然数の文字列です。これをint型配列で順番に「12」、「345」、「678」というように格納するというのが目的です。
定義するメソッドは、引数String s、返却値int[]というメソッドです。今回は3つのメソッドを作りました。大まかにいうと以下の3つの方法のメソッドです。
- String#split()メソッドを利用する
- String#indexOf()メソッドを利用する
- String#charAt()メソッドを利用する
分割というとsplitを使うのが一般的だと思います。残り2つはふと思いついた方法です。上記の方法について、速さを比較するということをやりました。
String#split()
String[] split(String regex)は引数が正規表現で、返却値が分割後のString配列です。この返却値をint型配列に格納しなおすという方法を使いました。この時の変換はIntegerクラスに用意されているstatic int parseInt(String s)を使いました。
メソッドの内容は以下です。
private final String regex = "\\s";
String[] numStrs = s.split(regex); int[] nums = new int[numStrs.length]; for (int i = 0; i < numStrs.length; i++) { nums[i] = Integer.parseInt(numStrs[i]); } return nums;
引数sに対して、splitを呼び出します。そのあと、int型配列を定義して、for文で各要素に代入操作をしています。
String#indexOf()
次に思いついたのはString#indexOf()を使う方法です。int indexOf(int ch)は引数で指定された文字が最初に出現する位置のインデックスを返します。半角スペースの位置を調べて、String substring(int beginIndex, int endIndex)を使うことを考えました。
private final char separator = ' ';
List<String> numsList = new ArrayList<>(); int index = s.indexOf(separator); while (index >= 0) { numsList.add(s.substring(0, index)); s = s.substring(index + 1); index = s.indexOf(separator); } numsList.add(s); int[] nums = new int[numsList.size()]; for (int i = 0; i < nums.length; i++) { nums[i] = Integer.parseInt(numsList.get(i)); } return nums;
半角スペースの位置を取得した後は、変数sにその位置から最後までの部分文字列を格納しています。この方法で繰り返し処理をやっています。部分文字列は最終的にはスペース区切りの一番最後の部分が残るため、while文が終わった後に「numsList.add(s);」を実行しています。
String#charAt()
最後に思いついたのは、String#charAt()メソッドを使う方法です。一文字ずつ取り出して、スペースまでひとつのint型変数に格納するという方針です。
private final char separator = ' ';
List<Integer> numsList = new ArrayList<>(); int n = 0; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == separator) { numsList.add(n); n = 0; } else { n *= 10; n += c - '0'; } } numsList.add(n); int[] nums = new int[numsList.size()]; for (int i = 0; i < nums.length; i++) { nums[i] = numsList.get(i); } return nums;
変数nが区切りまでの一つ分の自然数を格納するための変数です。取り出す文字は数字としては一桁なので、10倍したあとに加算するということをしています。半角スペースの場合、Listに格納して、nを0に戻すということをしています。この方法では繰り返し処理後の変数nには最後の自然数が格納されることになるため、「numsList.add(n);」を最後に実行しています。ListにはInteger型を格納しているため、最後にint型に格納しています。
速さの比較
以下、速さの比較をした時のソース全体です。「12 345 678」の分割を300000回繰り返して、処理にかかった時間のミリ秒を出力しています。
package practice; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Hello20170108 { // 計測用 private interface MyFunction { public abstract int[] get(String s); } private void printProcessingTime(MyFunction f) { String s = "12 345 678"; int count = 300000; long start, end; start = System.currentTimeMillis(); for (int i = 0; i < count; i++) { f.get(s); } end = System.currentTimeMillis(); System.out.printf("出力確認:%s, 計測 %3d: ", Arrays.toString(f.get(s)), end - start); System.out.println(); } // 計測 public static void main(String[] args) { new Hello20170108().test(); } private final String regex = "\\s"; private final char separator = ' '; private void test() { // test1 printProcessingTime(s -> { String[] numStrs = s.split(regex); int[] nums = new int[numStrs.length]; for (int i = 0; i < numStrs.length; i++) { nums[i] = Integer.parseInt(numStrs[i]); } return nums; }); // test2 printProcessingTime(s -> { List<String> numsList = new ArrayList<>(); int index = s.indexOf(separator); while (index >= 0) { numsList.add(s.substring(0, index)); s = s.substring(index + 1); index = s.indexOf(separator); } numsList.add(s); int[] nums = new int[numsList.size()]; for (int i = 0; i < nums.length; i++) { nums[i] = Integer.parseInt(numsList.get(i)); } return nums; }); // test3 printProcessingTime(s -> { List<Integer> numsList = new ArrayList<>(); int n = 0; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == separator) { numsList.add(n); n = 0; } else { n *= 10; n += c - '0'; } } numsList.add(n); int[] nums = new int[numsList.size()]; for (int i = 0; i < nums.length; i++) { nums[i] = numsList.get(i); } return nums; }); } }
結果の数値は毎回少し変わりますが、以下が一例です。
出力確認:[12, 345, 678], 計測 519: 出力確認:[12, 345, 678], 計測 164: 出力確認:[12, 345, 678], 計測 53:
splitを使う最初の方法よりも最後の方法の方が、かかった時間が10分1ほどという結果になりました。