Java и C # реализация String - PullRequest
       0

Java и C # реализация String

8 голосов
/ 08 сентября 2011

В реализации Java и C # String является ли основная информация массивом char с нулевым символом в конце, как в C / C ++?

(В дополнение к другой информации, такой как размер и т. Д.)

Ответы [ 3 ]

16 голосов
/ 08 сентября 2011

Нет. Это последовательность 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 быть очень эффективным - повторно использовать тот же байтовый массив, но с другой длиной (и смещением, если ваша реализация виртуальной машины не может указывать на массив).

UTF-16 (16-битный формат преобразования Unicode) - это кодировка символов для Unicode, способная кодировать 1 112 064 числа (называемых кодовыми точками) в кодовом пространстве Unicode от 0 до 0x10FFFF. Он выдает результат переменной длины, состоящий из одной или двух 16-битных кодовых единиц на кодовую точку.

С Javadoc

String представляет строку в формате UTF-16, в которой дополнительные символы представлены суррогатными парами (см. Раздел Представления символов Unicode в классе Character для получения дополнительной информации). Значения индекса относятся к единицам кодов символов, поэтому дополнительный символ использует две позиции в строке.

C # System.String определяется аналогично

Каждый символ Unicode в строке определяется скалярным значением Unicode, также называемым кодовой точкой Unicode или порядковым (числовым) значением символа Unicode. Каждая кодовая точка кодируется с использованием кодировки UTF-16, а числовое значение каждого элемента кодирования представляется символом. Результирующая коллекция объектов Char составляет String.

Я не уверен, что C # защищает от осиротевших суррогатов, но приведенный выше текст, кажется, смешивает термины «скалярное значение» и «кодовая точка», что сбивает с толку. скалярное значение определяется таким образом: unicode.org:

Любая кодовая точка Unicode, за исключением кодов с высоким и низким суррогатом

Java определенно использует представление кодовой точки и не пытается защититься от недопустимых скалярных значений в строках.

«Неизменность и постоянство строк» ​​ объясняет преимущества эффективности этого представления.

Одно из преимуществ неизменяемых типов данных, о которых я говорил здесь ранее, заключается в том, что они не только неизменяемы, но и «постоянны». Под «постоянным» я подразумеваю неизменный тип данных, так что общие операции с этим типом (например, добавление нового элемента в очередь или удаление элемента из дерева) могут повторно использовать большую часть или всю память существующих данных. состав. Поскольку все они неизменны, вы можете повторно использовать их части, не беспокоясь о том, что они могут измениться на вас.

EDIT: Вышесказанное верно концептуально и на практике, но виртуальные машины и CLR могут свободно делать вещи по-разному в определенных ситуациях.

Спецификация языка Java требует, чтобы строки располагались определенным образом в .class файлах, а его тип JNI jstring абстрагируется в детали представления памяти, так что теоретически виртуальная машина может представлять строку в памяти в виде строки UTF-8 с NUL-концом и двухбайтовой формой, используемой для встроенных символов NUL вместо представления int32 length и uint16[] bytes, которое позволяет для эффективного произвольного доступа к кодовым единицам.

Виртуальные машины на практике этого не делают. «Самая дорогая однобайтовая ошибка» утверждает, что NUL-завершенные строки были огромной ошибкой в ​​C, поэтому я сомневаюсь, что виртуальные машины примут их внутренне из соображений эффективности.

Лучший кандидат, который мне удалось найти, - это использование в C / Unix / Posix текстовых строк с NUL-завершением. Выбор был действительно прост: должен ли язык C представлять строки в виде кортежа адрес + длина или просто как адрес с магическим символом (NUL), отмечающим конец?

...

Немного подумав о системах виртуальной памяти, мы решили этот вопрос. Оптимизация перемещения строки байтов известной длины может использовать всю ширину шин памяти и строк кэша, не затрагивая область памяти, которая не является частью строки источника или назначения.

Одним из примеров является libc FreeBSD, где реализация bcopy (3) / memcpy (3) будет перемещать как можно больше данных в кусках «unsigned long», обычно 32 или 64 бита, а затем «очищать любые завершающие байты» "как описано в комментарии, с байтовыми операциями. 2

Если исходная строка завершена NUL, однако, попытка получить к ней доступ в единицах, превышающих байты, может привести к попытке чтения символов после NUL. Если символ NUL является последним байтом страницы [виртуальной памяти], а следующая страница [виртуальной памяти] не определена, это может привести к смерти процесса из-за необоснованной ошибки «страница отсутствует».

10 голосов
/ 08 сентября 2011

В качестве детали реализации, строка в реализации Microsoft CLR размещена в памяти почти так же, как BSTR была в COM. (Подробнее о BSTR см. http://blogs.msdn.com/b/ericlippert/archive/2003/09/12/52976.aspx.)

То есть строка состоит из четырех байтов, содержащих длину, за которой следует такое количество двухбайтовых символов UTF-16, за которыми следуют два байта с нулем.

Конечно, необязательно заканчивать строку с префиксом длины нулевым символом, но это, безусловно, удобно, особенно если учесть сценарии, в которых необходимо взаимодействовать между программами C # и неуправляемые программы C ++ или VB6. Иногда маршаллер может сэкономить на копировании, поскольку знает, что строка уже имеет формат с нулевым символом в конце.

Как я уже сказал, это деталь реализации; на это не стоит полагаться.

Я не знаю, что делает Java.

1 голос
/ 08 сентября 2011

Я не могу говорить за C #, но источник Java в String говорит, что нет. Информация о размере массива хранится в массиве, что не требует нулевого завершения.

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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...