ความซับซ้อนของเวลาของString#substring()
วิธีการใน Java คืออะไร?
ความซับซ้อนของเวลาของString#substring()
วิธีการใน Java คืออะไร?
คำตอบ:
คำตอบใหม่
เมื่ออัปเดต 6 ภายในอายุการใช้งานของ Java 7 พฤติกรรมของการsubstring
เปลี่ยนแปลงในการสร้างสำเนา - ดังนั้นทุกString
อ้างถึงchar[]
ที่ไม่ได้ใช้ร่วมกับวัตถุอื่น ๆ เท่าที่ฉันทราบ เมื่อถึงจุดนั้นsubstring()
กลายเป็นการดำเนินการ O (n) โดยที่ n คือตัวเลขในสตริงย่อย
คำตอบเก่า: pre-Java 7
ไม่มีเอกสาร - แต่ในทางปฏิบัติ O (1) หากคุณถือว่าไม่จำเป็นต้องมีการเก็บขยะ ฯลฯ
เพียงแค่สร้างString
วัตถุใหม่ที่อ้างถึงพื้นฐานเดียวกันchar[]
แต่มีค่าชดเชยและจำนวนที่แตกต่างกัน ดังนั้นต้นทุนคือเวลาที่ใช้ในการตรวจสอบความถูกต้องและสร้างออบเจ็กต์ใหม่ (ขนาดเล็กพอสมควร) เพียงชิ้นเดียว นั่นคือ O (1) เท่าที่สมเหตุสมผลที่จะพูดถึงความซับซ้อนของการดำเนินการซึ่งอาจแตกต่างกันไปตามเวลาตามการรวบรวมขยะแคช CPU เป็นต้นโดยเฉพาะอย่างยิ่งมันไม่ได้ขึ้นอยู่กับความยาวของสตริงดั้งเดิมหรือสตริงย่อยโดยตรง .
มันเป็น O (1) ใน Java เวอร์ชันเก่า - ตามที่จอนกล่าวไว้มันเพิ่งสร้างสตริงใหม่ที่มีอักขระพื้นฐานเดียวกัน [] และออฟเซ็ตและความยาวต่างกัน
อย่างไรก็ตามสิ่งนี้ได้เปลี่ยนแปลงไปแล้วโดยเริ่มจากการอัปเดต Java 7 6
การแบ่งปัน char [] ถูกตัดออกและช่องออฟเซ็ตและความยาวจะถูกลบออก สตริงย่อย () ตอนนี้เพียงแค่คัดลอกอักขระทั้งหมดลงในสตริงใหม่
Ergo สตริงย่อยคือ O (n) ใน Java 7 update 6
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;
}
}
}
ผลลัพธ์ในการดำเนินการใน Java 8 คือ:
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
การพิสูจน์ฟังก์ชันสตริงย่อยขึ้นอยู่กับความยาวของสตริงย่อยที่ร้องขอไม่ใช่ความยาวของสตริง
O (1) เนื่องจากไม่มีการคัดลอกสตริงต้นฉบับมันจึงสร้างออบเจ็กต์ Wrapper ใหม่พร้อมข้อมูลออฟเซ็ตที่แตกต่างกัน
ตัดสินด้วยตัวคุณเองจากการติดตาม แต่ข้อเสียด้านประสิทธิภาพของ 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) หรือไม่ขึ้นอยู่กับ หากคุณแค่อ้างอิง String เดียวกันในหน่วยความจำลองนึกภาพString ที่ยาวมากคุณสร้างสตริงย่อยและหยุดการอ้างอิงสตริงที่ยาว จะดีไหมถ้าปล่อยความทรงจำไว้นาน
ก่อน 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/