簡體   English   中英

Java和C#的String實現

[英]Java's and C#'s implementation of String

String的Java和C#實現中,底層信息是一個以C / C ++為單位的以null結尾的char數組嗎?

(除了尺寸等其他信息外)

不。它是一系列UTF-16代碼單元和長度。 Java和C#字符串可以包含嵌入的NUL。

每個UTF-16代碼單元占用兩個字節,因此您可以將字符串"\\n\\0\\n"視為:

{
  length: 3,  // 3 pairs of bytes == 3 UTF-16 code units
  bytes:  [0, 10, // \n
           0, 0,  // \0
           0, 10] // \n
}

請注意,在最后一個字節bytes不為0的length字段告訴很多字節是如何被使用。 這允許substring非常有效 - 重用相同的字節數組,但具有不同的長度(如果您的VM實現不能指向數組,則返回偏移量)。

UTF-16(16位Unicode轉換格式)是Unicode的字符編碼,能夠在Unicode代碼空間中編碼1,112,064個數字(稱為代碼點),范圍從0到0x10FFFF。 它產生每個代碼點一個或兩個16位代碼單元的可變長度結果。

來自javadoc

String表示UTF-16格式的字符串,其中補充字符由代理項對表示(有關詳細信息,請參閱Character類中的Unicode字符表示形式一節)。 索引值是指char代碼單元,因此補充字符在String中使用兩個位置。

C# System.String的定義類似

字符串中的每個Unicode字符都由Unicode標量值定義,也稱為Unicode代碼點或Unicode字符的序數(數字)值。 每個代碼點使用UTF-16編碼進行編碼,並且編碼的每個元素的數值由Char表示。 生成的Char對象集合構成String。

我不確定C#是否可以防止孤兒代理,但上面的文字似乎混淆了“標量值”和“代碼點”這兩個令人困惑的術語。 因此unicode.org定義了標量值

除高代理和低代理代碼點之外的任何Unicode代碼點

Java絕對采用代碼點視圖,並不試圖防止字符串中的無效標量值。

“字符串不變性和持久性”解釋了這種表示的效率優勢。

我之前談到的不可變數據類型的一個好處是它們不僅是不可變的,它們也是“持久的”。 “持久性”是指一種不可變的數據類型,這種類型的常見操作(如向隊列添加新項或從樹中刪除項)可以重用現有數據的大部分或全部內存結構體。 因為它是完全不可變的,所以你可以重復使用它的部件,而不必擔心它們會改變你。

編輯:以上在概念上和實踐中都是正確的,但VM和CLR在某些情況下可以自由地做不同的事情。

Java語言規范要求字符串在.class文件中以某種方式布局 ,並且其JNI jstring類型抽象出內存中的表示細節,因此理論上VM可以將內存中的字符串表示為NUL終止的UTF- 8字符串,雙字節形式,用於嵌入的NUL字符,而不是int32 lengthuint16[] bytes表示,允許有效隨機訪問代碼單元。

但VM在實踐中並沒有這樣做。 “最昂貴的單字節錯誤”認為NUL終止的字符串在C中是一個巨大的錯誤,所以我懷疑虛擬機會出於效率原因在內部采用它們。

我能夠提出的最佳候選者是使用NUL終止的文本字符串的C / Unix / Posix。 選擇非常簡單:C語言應該將字符串表示為地址+長度元組,還是將帶有魔術字符(NUL)的地址標記為結尾?

...

思考一下虛擬內存系統為我們解決了這個問題。 優化已知長度字節串的移動可以利用存儲器總線和高速緩存行的全寬度,而不會觸及不屬於源或目標字符串的存儲器位置。

一個例子是FreeBSD的libc,其中bcopy(3)/ memcpy(3)實現將在“unsigned long”(通常為32或64位)的塊中移動盡可能多的數據,然后“清除任何尾隨字節”作為注釋用字節操作描述它

但是,如果源字符串被NUL終止,則嘗試以大於字節為單位訪問它可能會嘗試在NUL之后讀取字符。 如果NUL字符是[虛擬內存]頁面的最后一個字節,並且未定義下一個[虛擬內存]頁面,則會導致進程死於不合理的“頁面不存在”錯誤。

作為一個實現細節,CLR的Microsoft實現中的字符串在內存中布局與BSTR在COM中幾乎相同。 (有關BSTR的詳細信息,請參見http://blogs.msdn.com/b/ericlippert/archive/2003/09/12/52976.aspx 。)

也就是說,一個字符串被布置為包含長度的四個字節,接着是許多兩個字節的UTF-16字符,后跟兩個零字節。

當然,沒有必要以零字符結束長度為前綴的字符串,但這樣做當然很方便,特別是在考慮必須在C#程序和非托管C ++或VB6程序之間進行互操作的情況時。 marshaller有時可以節省一些復制,因為它知道字符串已經是以空終止格式。

正如我所說,這是一個實施細節; 你不應該依賴它。

我不知道Java做了什么。

我不能代表C#,但Java的字符串來源說不。 數組的大小信息存儲在數組中,不需要空終止。

public final class String implements java.io.Serializable, Comparable<String>, CharSequence
{
    /** The value is used for character storage. */
    private final char value[];

    /** The offset is the first index of the storage that is used. */
    private final int offset;

    /** The count is the number of characters in the String. */
    private final int count;

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    // ... rest of class
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM