String#substring()
Javaのメソッドの時間計算量はどれくらいですか?
String#substring()
Javaのメソッドの時間計算量はどれくらいですか?
回答:
新しい答え
Java 7の存続期間内の更新6の時点でsubstring
、コピーを作成するために動作が変更されました。したがって、私が知る限り、すべてが他のオブジェクトと共有されていないをString
参照します。したがって、その時点で、はO(n)演算になりました。ここで、nは部分文字列の数値です。char[]
substring()
古い答え:Java7以前
文書化されていませんが、実際には、ガベージコレクションが不要であると想定している場合はO(1)などです。
String
同じ原資産を参照しているchar[]
が、オフセットとカウントの値が異なる新しいオブジェクトを作成するだけです。したがって、コストは、検証を実行し、単一の新しい(適度に小さい)オブジェクトを構築するのにかかる時間です。ガベージコレクションやCPUキャッシュなどに基づいて時間とともに変化する可能性のある操作の複雑さについて話すのが賢明な限り、これはO(1)です。特に、元の文字列または部分文字列の長さに直接依存しません。 。
古いバージョンのJavaではO(1)でした。Jonが述べたように、基になるchar []が同じで、オフセットと長さが異なる新しい文字列を作成しただけです。
ただし、これは実際にはJava 7 update6から変更されています。
char []の共有が削除され、offsetフィールドとlengthフィールドが削除されました。substring()は、すべての文字を新しい文字列にコピーするだけです。
エルゴ、Java 7アップデート6では部分文字列はO(n)です
char[]
...
今では線形の複雑さです。これは、部分文字列のメモリリークの問題を修正した後です。
したがって、Java 1.7.0_06から、String.substringの複雑さが一定ではなく線形になったことを思い出してください。
ジョンの答えに証拠を追加します。同じ疑問があり、文字列の長さが部分文字列関数に影響を与えるかどうかを確認したいと思いました。どのパラメータ部分文字列が実際に依存しているかを確認するために、次のコードを記述しました。
import org.apache.commons.lang.RandomStringUtils;
public class Dummy {
private static final String pool[] = new String[3];
private static int substringLength;
public static void main(String args[]) {
pool[0] = RandomStringUtils.random(2000);
pool[1] = RandomStringUtils.random(10000);
pool[2] = RandomStringUtils.random(100000);
test(10);
test(100);
test(1000);
}
public static void test(int val) {
substringLength = val;
StatsCopy statsCopy[] = new StatsCopy[3];
for (int j = 0; j < 3; j++) {
statsCopy[j] = new StatsCopy();
}
long latency[] = new long[3];
for (int i = 0; i < 10000; i++) {
for (int j = 0; j < 3; j++) {
latency[j] = latency(pool[j]);
statsCopy[j].send(latency[j]);
}
}
for (int i = 0; i < 3; i++) {
System.out.println(
" Avg: "
+ (int) statsCopy[i].getAvg()
+ "\t String length: "
+ pool[i].length()
+ "\tSubstring Length: "
+ substringLength);
}
System.out.println();
}
private static long latency(String a) {
long startTime = System.nanoTime();
a.substring(0, substringLength);
long endtime = System.nanoTime();
return endtime - startTime;
}
private static class StatsCopy {
private long count = 0;
private long min = Integer.MAX_VALUE;
private long max = 0;
private double avg = 0;
public void send(long latency) {
computeStats(latency);
count++;
}
private void computeStats(long latency) {
if (min > latency) min = latency;
if (max < latency) max = latency;
avg = ((float) count / (count + 1)) * avg + (float) latency / (count + 1);
}
public double getAvg() {
return avg;
}
public long getMin() {
return min;
}
public long getMax() {
return max;
}
public long getCount() {
return count;
}
}
}
Java8での実行時の出力は次のとおりです。
Avg: 128 String length: 2000 Substring Length: 10
Avg: 127 String length: 10000 Substring Length: 10
Avg: 124 String length: 100000 Substring Length: 10
Avg: 172 String length: 2000 Substring Length: 100
Avg: 175 String length: 10000 Substring Length: 100
Avg: 177 String length: 100000 Substring Length: 100
Avg: 1199 String length: 2000 Substring Length: 1000
Avg: 1186 String length: 10000 Substring Length: 1000
Avg: 1339 String length: 100000 Substring Length: 1000
部分文字列関数の証明は、文字列の長さではなく、要求された部分文字列の長さに依存します。
以下から自分で判断してください。ただし、Javaのパフォーマンスの欠点は、ここでは文字列の部分文字列ではなく、別の場所にあります。コード:
public static void main(String[] args) throws IOException {
String longStr = "asjf97zcv.1jm2497z20`1829182oqiwure92874nvcxz,nvz.,xo" +
"aihf[oiefjkas';./.,z][p\\°°°°°°°°?!(*#&(@*&#!)^(*&(*&)(*&" +
"fasdznmcxzvvcxz,vc,mvczvcz,mvcz,mcvcxvc,mvcxcvcxvcxvcxvcx";
int[] indices = new int[32 * 1024];
int[] lengths = new int[indices.length];
Random r = new Random();
final int minLength = 6;
for (int i = 0; i < indices.length; ++i)
{
indices[i] = r.nextInt(longStr.length() - minLength);
lengths[i] = minLength + r.nextInt(longStr.length() - indices[i] - minLength);
}
long start = System.nanoTime();
int avoidOptimization = 0;
for (int i = 0; i < indices.length; ++i)
//avoidOptimization += lengths[i]; //tested - this was cheap
avoidOptimization += longStr.substring(indices[i],
indices[i] + lengths[i]).length();
long end = System.nanoTime();
System.out.println("substring " + indices.length + " times");
System.out.println("Sum of lengths of splits = " + avoidOptimization);
System.out.println("Elapsed " + (end - start) / 1.0e6 + " ms");
}
出力:
部分文字列32768回 分割の長さの合計= 1494414 経過2.446679ミリ秒
O(1)かどうかは、によって異なります。メモリ内の同じ文字列を参照するだけで、非常に長い文字列を想像すると、部分文字列を作成し、長い文字列の参照を停止します。長いもののためにメモリを解放するのはいいことではないでしょうか?
Java 1.7.0_06より前:O(1)。
後のJava 1.7.0_06:O(N)。これは、メモリリークのために変更されました。フィールドoffset
とcount
がStringから削除された後、 部分文字列の実装はO(n)になりました。
詳細については、http://java-performance.info/changes-to-string-java-1-7-0_06/を参照してください。