ความยาวสูงสุดของสตริงใน Java - เมธอด length ()


150

ในJavaStringวัตถุขนาดใหญ่สุดที่อาจมีหมายถึงlength()การเรียกวิธีการคืออะไร?

ฉันรู้ว่าlength()คืนขนาดของStringa char [];


5
ในขณะที่ความยาวของ a ตามStringหลักทฤษฏีInteger.MAX_VALUEความยาวของสตริงตัวอักษรในแหล่งที่ปรากฏจะถูก จำกัด เพียง65535 ไบต์ไบต์ของข้อมูล UTF-8
200_success

คำตอบ:


169

เมื่อพิจารณาถึงStringคลาสของlengthเมธอดจะส่งกลับintค่าความยาวสูงสุดที่จะถูกส่งคืนโดยเมธอดนั้นInteger.MAX_VALUEคือ2^31 - 1(หรือประมาณ 2 พันล้าน)

ในแง่ของความยาวและการจัดทำดัชนีของอาร์เรย์ (เช่นchar[]ซึ่งอาจเป็นวิธีการใช้การแทนข้อมูลภายในสำหรับStrings) บทที่ 10: อาร์เรย์ของข้อกำหนดภาษา Java, Java SE 7 Editionกล่าวว่า:

ตัวแปรที่อยู่ในอาร์เรย์ไม่มีชื่อ แต่จะถูกอ้างอิงโดยนิพจน์การเข้าถึงอาร์เรย์ที่ใช้ค่าดัชนีจำนวนเต็มแบบไม่ลบ ตัวแปรเหล่านี้เรียกว่า ส่วนประกอบของอาเรย์ ถ้าอาเรย์มีnส่วนประกอบเราจะบอกว่าnเป็น ความยาวของอาเรย์ ส่วนประกอบของอาร์เรย์อ้างอิงโดยใช้ดัชนีจำนวนเต็มตั้งแต่0ถึงn - 1รวม

นอกจากนี้การจัดทำดัชนีจะต้องเป็นไปตามintค่าที่กล่าวไว้ในมาตรา 10.4 :

อาร์เรย์จะต้องจัดทำดัชนีโดยintค่า

ดังนั้นจึงปรากฏว่าข้อ จำกัด นั้นแน่นอน2^31 - 1เนื่องจากเป็นค่าสูงสุดสำหรับค่าที่ไม่ใช่intค่าลบ

อย่างไรก็ตามอาจมีข้อ จำกัด อื่น ๆ เช่นขนาดที่สามารถจัดสรรได้สูงสุดสำหรับอาร์เรย์


26
Integer.MAX_VALUE คือ 2 ^ 31-1 จริง ๆ แล้ว :)
Michael Myers

1
สุดยอดคำตอบชาย! ฉันดูที่ซอร์สโค้ด String.java และถูกต้อง 'count' เป็นตัวแปร int ที่ส่งคืนความยาวของอาร์เรย์ char และอาร์เรย์ char ถูกเก็บไว้ในตัวแปร 'value' (เป็น char []) ขนาดของสตริงอาจประมาณ 2GB แน่นอนอาจมีข้อ จำกัด ในการจัดสรรขนาดหน่วยความจำดังกล่าว ขอบคุณ!
taichi

5
ฉันก็พยายามกำหนดตัวอักษรสตริงใน java โปรแกรม Hello World ซึ่งเป็นนานกว่า 65546. javacให้เกิดข้อผิดพลาดเกี่ยวกับที่เป็นตัวหนังสือยาวเกินไป:javac HelloWorld.java 2>&1|head -c 80 HelloWorld.java:3: constant string too long
dlamblin

2
@dlamblin: เสียงที่เหมือนข้อ จำกัด ของjavacสำหรับString ตัวอักษร (ไม่ใช่Stringวัตถุ) ที่ผมไม่สามารถหาอ้างอิงถึงขีด จำกัด ของขนาดStringตัวอักษรในข้อมูลจำเพาะภาษา Java และ JVM ข้อมูลจำเพาะ ฉันพยายามสร้างStringตัวอักษรที่มีขนาดใหญ่กว่า 100,000 ตัวอักษรและคอมไพเลอร์ Eclipse ไม่มีปัญหาในการรวบรวม (และการเรียกใช้โปรแกรมก็สามารถที่จะแสดงให้เห็นว่าตัวอักษรมีString.lengthขนาดใหญ่กว่า 100,000.)
coobird

3
@Premraj มันเป็นสามปีที่ผ่านมาดังนั้นฉันต้องคิดเกี่ยวกับมัน ;) สิ่งที่ฉันหมายถึงคือ; เพื่อสร้างสตริงที่มีขนาดใหญ่ที่สุดที่คุณต้องการหน่วยความจำจำนวนมากอาจมากกว่าที่คุณมีอยู่แล้ว คุณต้องมีสองไบต์ต่อตัวอักษร ~ 4GB แต่คุณต้องสร้างมันขึ้นมาจาก StringBuilder หรืออักขระ [] ซึ่งหมายความว่าคุณต้องการอีกสองไบต์ต่อตัวอักษรเพื่อสร้างขึ้นในสถานที่แรกเช่นอีก ~ 4 GB (อย่างน้อยชั่วคราว)
Peter Lawrey

25

java.io.DataInput.readUTF()และjava.io.DataOutput.writeUTF(String)บอกว่าStringวัตถุนั้นถูกแทนด้วยข้อมูลความยาวสองไบต์และการแทนUTF-8 ที่ดัดแปลงของตัวละครทุกตัวในสตริง นี้สรุปว่าความยาวของเชือกถูก จำกัด ด้วยจำนวนไบต์ของ UTF-8 เป็นตัวแทนการปรับเปลี่ยนของสตริงเมื่อใช้กับและDataInputDataOutput

นอกจากนี้ข้อมูลจำเพาะที่CONSTANT_Utf8_infoพบในข้อมูลจำเพาะเครื่องเสมือน Java จะกำหนดโครงสร้างดังต่อไปนี้

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

คุณจะพบว่าขนาดของ 'ความยาว' เป็นสองไบต์

ว่าประเภทการกลับมาของวิธีการบางอย่าง (เช่นString.length()) คือไม่ได้หมายความว่าค่าสูงสุดที่อนุญาตคือint Integer.MAX_VALUEในกรณีส่วนใหญ่intจะเลือกด้วยเหตุผลด้านประสิทธิภาพเท่านั้น ข้อกำหนดภาษาจาวากล่าวว่าจำนวนเต็มที่มีขนาดเล็กกว่าของintจะถูกแปลงเป็นintก่อนการคำนวณ (ถ้าหน่วยความจำของฉันทำหน้าที่ฉันอย่างถูกต้อง) และเป็นเหตุผลหนึ่งที่เลือกintเมื่อไม่มีเหตุผลพิเศษ

ความยาวสูงสุดที่เวลารวบรวมคือสูงสุด 65536 โปรดทราบอีกครั้งว่าความยาวคือจำนวนไบต์ของการแทนUTF-8 ที่ปรับเปลี่ยนไม่ใช่จำนวนตัวอักษรในStringวัตถุ

Stringวัตถุอาจสามารถมีอักขระมากขึ้นในขณะทำงาน อย่างไรก็ตามหากคุณต้องการใช้Stringวัตถุด้วยDataInputและDataOutputอินเตอร์เฟสมันจะดีกว่าถ้าจะหลีกเลี่ยงการใช้Stringวัตถุที่ยาวเกินไป ผมพบว่าข้อ จำกัด นี้เมื่อผมดำเนินการเทียบเท่า Objective-C ของและDataInput.readUTF()DataOutput.writeUTF(String)


1
นี่ควรเป็นคำตอบเริ่มต้น
Nick

20

เนื่องจากอาร์เรย์ต้องมีการทำดัชนีด้วยจำนวนเต็มความยาวสูงสุดของอาร์เรย์คือInteger.MAX_INT(2 31 -1 หรือ 2 147 483 647) นี่คือการสมมติว่าคุณมีหน่วยความจำเพียงพอที่จะเก็บอาร์เรย์ขนาดนั้นแน่นอน


9

ฉันมี iMac 2010 พร้อม RAM ขนาด 8GB ใช้ Eclipse Neon.2 Release (4.6.2) พร้อม Java 1.8.0_25 ด้วยอาร์กิวเมนต์ VM -Xmx6g ฉันรันโค้ดต่อไปนี้:

StringBuilder sb = new StringBuilder();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
    try {
        sb.append('a');
    } catch (Throwable e) {
        System.out.println(i);
        break;
    }
}
System.out.println(sb.toString().length());

ภาพพิมพ์นี้:

Requested array size exceeds VM limit
1207959550

ดังนั้นดูเหมือนว่าขนาดอาร์เรย์สูงสุดคือ ~ 1,207,959,549 จากนั้นฉันก็รู้ว่าเราไม่สนใจจริง ๆ ว่า Java มีหน่วยความจำไม่เพียงพอ: เราแค่มองหาขนาดอาเรย์สูงสุด (ซึ่งดูเหมือนจะเป็นค่าคงที่ที่ใดที่หนึ่ง) ดังนั้น:

for (int i = 0; i < 1_000; i++) {
    try {
        char[] array = new char[Integer.MAX_VALUE - i];
        Arrays.fill(array, 'a');
        String string = new String(array);
        System.out.println(string.length());
    } catch (Throwable e) {
        System.out.println(e.getMessage());
        System.out.println("Last: " + (Integer.MAX_VALUE - i));
        System.out.println("Last: " + i);
    }
}

สิ่งที่พิมพ์:

Requested array size exceeds VM limit
Last: 2147483647
Last: 0
Requested array size exceeds VM limit
Last: 2147483646
Last: 1
Java heap space
Last: 2147483645
Last: 2

ดังนั้นดูเหมือนว่า max คือ Integer.MAX_VALUE - 2 หรือ (2 ^ 31) - 3

ป.ล. ฉันไม่แน่ใจว่าทำไมStringBuildermaxed out 1207959550ของฉันในขณะที่char[]maxed out ที่ (2 ^ 31) -3 ดูเหมือนว่าAbstractStringBuilderขนาดของภายในchar[]จะเพิ่มขึ้นเป็นสองเท่าดังนั้นอาจเป็นสาเหตุของปัญหา


1
การปฏิบัติที่มีประโยชน์มากสำหรับคำถาม
Pavlo Maistrenko


4

ประเภทการกลับมาของความยาว () วิธีการของคลาส String เป็นint

ความยาว int สาธารณะ ()

อ้างอิงhttp://docs.oracle.com/javase/7/docs/api/java/lang/String.html#length ()

ดังนั้นค่าสูงสุดของ int เป็น2147483647

String ถือเป็น char char ภายใน, ดังนั้นการสร้างดัชนีจึงอยู่ในช่วงสูงสุด ซึ่งหมายความว่าเราไม่สามารถจัดทำดัชนีสมาชิก 2147483648 ได้ดังนั้นความยาวสูงสุดของ String ใน java คือ 2147483647

ประเภทข้อมูลดั้งเดิม int คือ 4 ไบต์ (32 บิต) ใน java.As 1 บิต (MSB) ถูกใช้เป็นบิตสัญญาณช่วงถูก จำกัด ภายใน-2 ^ 31 ถึง 2 ^ 31-1 (-2147483648 ถึง 2147483647) เราไม่สามารถใช้ค่าลบสำหรับการจัดทำดัชนีดังนั้นช่วงที่เราสามารถใช้ได้คือตั้งแต่ 0 ถึง 2147483647


0

ตามที่กล่าวไว้ในคำตอบของ Takahiko Kawasaki , java แสดงถึง Unicode strings ในรูปแบบของUTF-8 ที่ดัดแปลงและใน JVM-Spec CONSTANT_UTF8_info โครงสร้าง JVM , 2 ไบต์จะถูกจัดสรรให้มีความยาว
เพื่อขยายคำตอบวิธีการของห้องสมุดASM jvm bytecodeประกอบด้วย:putUTF8

public ByteVector putUTF8(final String stringValue) {
    int charLength = stringValue.length();
    if (charLength > 65535) {   
   // If no. of characters> 65535, than however UTF-8 encoded length, wont fit in 2 bytes.
      throw new IllegalArgumentException("UTF8 string too large");
    }
    for (int i = 0; i < charLength; ++i) {
      char charValue = stringValue.charAt(i);
      if (charValue >= '\u0001' && charValue <= '\u007F') {
        // Unicode code-point encoding in utf-8 fits in 1 byte.
        currentData[currentLength++] = (byte) charValue;
      } else {
        // doesnt fit in 1 byte.
        length = currentLength;
        return encodeUtf8(stringValue, i, 65535);
      }
    }
    ...
}

แต่เมื่อทำการแมปรหัสจุด> 1byte มันเรียกencodeUTF8วิธี:

final ByteVector encodeUtf8(final String stringValue, final int offset, final int maxByteLength /*= 65535 */) {
    int charLength = stringValue.length();
    int byteLength = offset;
    for (int i = offset; i < charLength; ++i) {
      char charValue = stringValue.charAt(i);
      if (charValue >= 0x0001 && charValue <= 0x007F) {
        byteLength++;
      } else if (charValue <= 0x07FF) {
        byteLength += 2;
      } else {
        byteLength += 3;
      }
    }
   ...
}

ในแง่นี้ความยาวสตริงสูงสุดคือ 65535 ไบต์เช่นความยาวการเข้ารหัส utf-8 และไม่charนับ
คุณสามารถค้นหาช่วงรหัสจุดที่ได้รับการแก้ไขของ JVM ได้จากลิงก์ utf8 struct ด้านบน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.