Độ phức tạp về thời gian của String#substring()
phương thức trong Java là gì?
Độ phức tạp về thời gian của String#substring()
phương thức trong Java là gì?
Câu trả lời:
Câu trả lời mới
Tính cập nhật 6 trong đời Java 7, hành vi của substring
thay đổi để tạo ra một bản sao - vì vậy mỗi String
dùng để chỉ một char[]
mà là không chia sẻ với bất kỳ đối tượng khác, như xa như tôi biết. Vì vậy, tại thời điểm đó, substring()
đã trở thành một phép toán O (n) với n là các số trong chuỗi con.
Câu trả lời cũ: trước Java 7
Không có giấy tờ - nhưng trong thực tế O (1) nếu bạn cho rằng không cần thu gom rác, v.v.
Nó chỉ đơn giản là xây dựng một String
đối tượng mới tham chiếu đến cùng một cơ sở char[]
nhưng với các giá trị bù và đếm khác nhau. Vì vậy, chi phí là thời gian thực hiện xác nhận và xây dựng một đối tượng mới (nhỏ hợp lý). Đó là O (1) khi nói về mức độ phức tạp của các hoạt động có thể thay đổi theo thời gian dựa trên việc thu gom rác, bộ nhớ đệm CPU, v.v. Đặc biệt, nó không phụ thuộc trực tiếp vào độ dài của chuỗi gốc hoặc chuỗi con .
Nó là O (1) trong các phiên bản Java cũ hơn - như Jon đã nói, nó chỉ tạo ra một Chuỗi mới với cùng một ký tự cơ bản [], và một độ lệch và độ dài khác.
Tuy nhiên, điều này đã thực sự thay đổi bắt đầu từ bản cập nhật Java 7 6.
Chia sẻ char [] đã bị loại bỏ và các trường độ dài và độ lệch cũng bị loại bỏ. substring () bây giờ chỉ cần sao chép tất cả các ký tự vào một chuỗi mới.
Ergo, chuỗi con là O (n) trong Java 7 cập nhật 6
char[]
...
Bây giờ nó phức tạp tuyến tính. Đây là sau khi khắc phục sự cố rò rỉ bộ nhớ cho chuỗi con.
Vì vậy, từ Java 1.7.0_06 hãy nhớ rằng String.substring hiện có độ phức tạp tuyến tính thay vì một hằng số.
Thêm bằng chứng cho câu trả lời của Jon. Tôi cũng có cùng nghi ngờ và muốn kiểm tra xem độ dài của chuỗi có bất kỳ ảnh hưởng nào đến hàm chuỗi con hay không. Đoạn mã sau đây được viết để kiểm tra xem chuỗi con tham số nào thực sự phụ thuộc vào.
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;
}
}
}
Đầu ra khi thực thi trong Java 8 là:
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
Chức năng chứng minh chuỗi con phụ thuộc vào độ dài của chuỗi con được yêu cầu chứ không phụ thuộc vào độ dài của chuỗi.
O (1) bởi vì không sao chép chuỗi gốc được thực hiện, nó chỉ tạo một đối tượng bao bọc mới với thông tin bù đắp khác nhau.
Đánh giá cho chính bạn từ việc theo dõi, nhưng những hạn chế về hiệu suất của Java nằm ở một nơi khác, không phải ở đây trong chuỗi con của một chuỗi. Mã:
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");
}
Đầu ra:
chuỗi con 32768 lần Tổng độ dài của các phần chia = 1494414 Đã trôi qua 2,446679 mili giây
Nếu nó là O (1) hay không, phụ thuộc. Nếu bạn chỉ tham chiếu cùng một Chuỗi trong bộ nhớ, thì hãy tưởng tượng một Chuỗi rất dài, bạn tạo chuỗi con và dừng tham chiếu chuỗi dài. Sẽ không tốt khi giải phóng bộ nhớ trong một thời gian dài?
Trước Java 1.7.0_06: O (1).
Sau Java 1.7.0_06: O (n). Điều này đã được thay đổi, do bộ nhớ bị rò rỉ. Sau khi các trường offset
và count
được xóa khỏi Chuỗi, việc triển khai chuỗi con trở thành O (n).
Để biết thêm chi tiết, vui lòng tham khảo: http://java-performance.info/changes-to-string-java-1-7-0_06/