Search code examples
javaudpdatagram

Java Why does the constructor for a DatagramPacket need the length of the byte array?


According to the javadocs very constructor in the DatagramPacket class requires an array of bytes and an integer less than or equal to the length of that array. e.g:

DatagramPacket(byte[] buf, int length)

All the examples i have seen simply pass the length attribute of the byte array like this:

byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

For this (common) use case it would have been simpler to access the length attribute of the passed byte array inside the constructor. Why is the length parameter explicitly required for all constructors?


Solution

  • The precise answer to the question 'why' is simply 'because'. The spec says so, and note that DatagramPacket has been part of java since the very very beginning (well, after oak, I guess): The javadoc is tagged with @since 1.0.

    All we can do is guess at what the original developer of this API was thinking.

    So I'll try to do just that:

    1. This API predates the convention to offer both (dataStore) as well as (dataStore, offset, length) variants (and no other variants). In, say, the new String(byteArray, offset, length, charset) constructor, and just about everywhere else this kind of concept shows up, you have to pick between either passing the entire datastore, OR passing both offset and length; you can never pass solely length and have offset default to 0. But you can here; that's because this convention wasn't a thing yet.

    2. The reason that the notion 'pass an offset and a length' exists at all, not just here but with string's byte-array based constructor, with OutputStream's write method which comes in both write(byte[] data) as well as write(byte[] data, int offset, int length) variants, and many, many other places in the java core libraries, is because often you have a larger buffer than the amount of data you have. If you don't yet know how much data you want to send in your datagram packet, you could generate all the data into a self-growing structure, like a ByteArrayOutputStream or perhaps a StringBuilder or an ArrayList<Byte>, write out all the data, then extract a properly sized byte[] array from it. Or, and this is VASTLY more efficient, you make a byte array that is definitely large enough to store whatever you're going to make, optionally reuse this using ThreadLocal if you must, and once you're done, only NOW do you know how large it actually had to be. Instead of making another byte array that is properly sized and copying all bytes in, just.. send the first X bytes of the 'buffer array', save yourself the memory and the copy operation. There are also reasons for not wanting to start at 0 in the same vein. Which is why DatagramPacket also has a (byte[] buf, int offset, int length) constructor, and this also explains why the parameter name of the byte array is in fact buf (short for buffer, and note how it isn't the more obvious name data).

    3. ByteBuffer is a concept that is specifically designed to do this well. I'm not sure why there still isn't a constructor in DatagramPacket to work with these, but datagrampacket itself predates bytebuffers by a decade or more. Had bytebuffers been part of the language from 1.0, this would probably have been done with bytebuffers, which fully represent this notion of having a fixed size buffer and being very efficient with having code that works with potentially unknown lengths of data to write/read into it at precisely the right position and no further than some limit in order to avoid having to make byte arrays all over the place.