2014-09-19 5 views
8
byte[] byteArray = Charset.forName("UTF-8").encode("hello world").array(); 
System.out.println(byteArray.length); 

Почему приведенная выше строка кода печатает 12, не должна ли она печатать 11 вместо этого?Какова длина строки, закодированной в ByteBuffer

+0

Я подозреваю, что сеть из-за этого, чтобы не использовать метод ByteBuffer.array без первого тщательно чтение документации. –

+0

Я очень удивлен, что у вас есть открытый доступ к базовому массиву вообще, вместо того, чтобы быть вынужденным использовать методы get и put. – azurefrog

ответ

11

Длиной массива размер ByteBuffer ' s, которая генерируется, но не равна количеству символов, которые вы кодируете. Давайте посмотрим, как мы выделяем память для ByteBuffer ...

Если просверлить в метод encode(), вы обнаружите, что CharsetEncoder#encode(CharBuffer) выглядит следующим образом:

public final ByteBuffer encode(CharBuffer in) 
    throws CharacterCodingException 
{ 
    int n = (int)(in.remaining() * averageBytesPerChar()); 
    ByteBuffer out = ByteBuffer.allocate(n); 
    ... 

По моему отладчиком, то averageBytesPerCharUTF_8$Encoder - 1.1, а на входе String - 11 символов. 11 * 1.1 = 12.1, а код бросает в общей сложности на int, когда он делает расчет, поэтому результирующий размер ByteBuffer равно 12.

+5

Вау, интересно, кто придумал этот средний показатель и какие данные они использовали.Так или иначе, это напоминает мне анекдот о статистике, который засунул голову в духовку и опустил ноги в морозильник, и объявляет себя в среднем комфортно теплым. –

1

Потому что он возвращает ByteBuffer. Это буфер вместимости (на самом деле это даже не из-за возможной нарезки), а не столько байтов. Это немного похоже на то, как malloc(10) может возвращать 32 байта памяти.

System.out.println(Charset.forName("UTF-8").encode("hello world").limit()); 

Это 11 (как и ожидалось).

+0

Я так не думаю. Если вы создали пустой массив, он по умолчанию отказался бы и, возможно, был бы больше, чем количество байтов, используемых после назначения, но в этом случае будет вызываться конструктор копирования, и я ожидаю, что массив инициализируется количество переданных символов. – ventsyv

+0

Когда вы создаете массив, '.length' будет длиной, которую вы просили (это в спецификации Java). В этом случае 'encode()' не говорит, что он будет делать, просто что он возвращает 'ByteBuffer' с закодированными вами символами. Другие впились в него, и это детализация реализации, поэтому это поведение будет даже отличаться между версиями JVM и реализациями. –

+0

Хорошо, это имеет смысл. – ventsyv

0
import java.nio.charset.*; 
public class ByteArrayTest { 
    public static void main(String[] args) { 
     String theString = "hello world"; 
     System.out.println(theString.length()); 
     byte[] byteArray = Charset.forName("UTF-8").encode(theString).array(); 
     System.out.println(byteArray.length); 
     for (int i = 0; i < byteArray.length; i++) { 
      System.out.println("Byte " + i + " = " + byteArray[i]); 
     } 
    } 
} 

Результаты:

C:\JavaTools>java ByteArrayTest 
11 
12 
Byte 0 = 104 
Byte 1 = 101 
Byte 2 = 108 
Byte 3 = 108 
Byte 4 = 111 
Byte 5 = 32 
Byte 6 = 119 
Byte 7 = 111 
Byte 8 = 114 
Byte 9 = 108 
Byte 10 = 100 
Byte 11 = 0 

Массив является нулевым байтом, как любой хороший C-строка будет.

(Но, видимо, реальная причиной является слоеным методом array. Это, вероятно, не следует использовать в «производстве» кода, кроме как с большой осторожностью.)

+3

Я не думаю, что конечный 0 байт является нулевым терминатором, так как неиспользуемое буферное пространство – Andreas

+0

@Andreas - Да, вы, вероятно, правы - [массив] (http://docs.oracle.com/javase/7 /docs/api/java/nio/ByteBuffer.html#array%28%29) - это полу-фиктивная операция, которая возвращает внутренний буфер ByteBuffer, и поэтому не сообщается, насколько он будет большой. –

+0

@Andreas: Java-среда исполнения (я полагаю) реализована на C, поэтому вполне возможно, что строки были тайно завершены с нулевым завершением. :-) –

Смежные вопросы