-2

I was just playing around with Java ByteBuffers and don't understand why the output is not correct.

import java.nio.ByteBuffer;

public class TestBuffers {
    public static void main(String[] args) {
        ByteBuffer byteBuffer = ByteBuffer.allocate(100);
        byteBuffer.put((byte)'H').put((byte)'e').put((byte)'l').put((byte)'l');
        System.out.println(byteBuffer.asCharBuffer().toString()); // should print "Hell"
    }
}

The above program should print "Hell" but it doesn't. but if i give positions in the put() call starting from 1 then it works, why?

Ankit Kumar
  • 1,433
  • 1
  • 16
  • 24

2 Answers2

5

There are some problems in your code and in your approach to that task:

  1. put() methods without the explicit index will update the buffer position. After four put operations the buffer position is 4.
  2. asCharBuffer() reuses the contents of the original buffer, starting from the current position in the origin buffer, which is 4 and the original buffer has no actual data starting from that position.
  3. flip() is indeed the proper action before trying to perform get operations after a sequence of put operations, but:
  4. In Java, char is a two-byte value, meaning that your original 4-byte buffer will be interpreted as a 2-char buffer, for example the value of the first char will be ( char ) ( ( ( ( byte ) 'H' ) << 8 ) + ( byte ) 'e' ).

Other that this, the ByteBuffer behaves exactly as expected and documented in its javadoc.

An example that fixes the coding problems:

ByteBuffer byteBuffer = ByteBuffer.allocate( 100 );
byteBuffer
        // explicitly converting each char into 2 bytes
        .put( ( byte ) ( 'H' >>> 8 ) ).put( ( byte ) 'H' ) 
        .put( ( byte ) ( 'e' >>> 8 ) ).put( ( byte ) 'e' )
        .put( ( byte ) ( 'l' >>> 8 ) ).put( ( byte ) 'l' )
        .put( ( byte ) ( 'l' >>> 8 ) ).put( ( byte ) 'l' )
        .put( ( byte ) ( 'o' >>> 8 ) ).put( ( byte ) 'o' );
// flipping the buffer to be able access the current content via get operations
byteBuffer.flip();
// reinterpreting the byte sequence as a char sequence
CharBuffer charBuffer = byteBuffer.asCharBuffer();
System.out.println( charBuffer.toString() );
Oleg Estekhin
  • 8,063
  • 5
  • 49
  • 52
4

The issue is that char is 16-bit, and byte is 8-bit. When you cast, you're losing some information. You need to insert two bytes for every char, and then flip the buffer, as per below:

import java.nio.ByteBuffer;

public class TestBuffers {
  ByteBuffer byteBuffer = ByteBuffer.allocate(100);

  byteBuffer.put((byte) ('H' & 0xFF00)).put((byte) ('H' & 0x00FF)).put((byte) ('E' & 0xFF00))
    .put((byte) ('E' & 0x00FF)).put((byte) ('L' & 0xFF00)).put((byte) ('L' & 0x00FF))
    .put((byte) ('L' & 0xFF00)).put((byte) ('L' & 0x00FF));
  byteBuffer.flip();
  System.out.println(byteBuffer.asCharBuffer().toString()); // should print "Hell"
 }
}
Evan Knowles
  • 7,426
  • 2
  • 37
  • 71
  • While this code will probably work for ASCII values, you are losing the high values (should be bit-shifting) – Dave May 23 '14 at 06:45
  • They are actually, but a `char` is always two bytes. This means that when they're converted back to a char, the H byte and the E byte will be combined together to form a character, generating the random garbage he was seeing. – Evan Knowles May 23 '14 at 06:45
  • @Dave True, but I was going for the minimal ASCII example :) – Evan Knowles May 23 '14 at 06:45
  • @EvanKnowles as Oleg's answer is more elaborate, choosing that as the answer. thanks for the help :) – Ankit Kumar May 23 '14 at 06:55