Java: How to pass byte[] by reference?
You can do it in .NET by using the keyword "ref". Is there any way to do so in Java?
What are you doing in your method? If you're merely populating an existing array, then you don't need pass-by-reference semantics - either in .NET or in Java. In both cases, the reference will be passed by value - so changes to the object will be visible by the caller. That's like telling someone the address of your house and asking them to deliver something to it - no problem.
If you really want pass-by-reference semantics, i.e. the caller will see any changes made to the parameter itself, e.g. setting it to null or a reference to a different byte array, then either method needs to return the new value, or you need to pass a reference to some sort of "holder" which contains a reference to the byte array, and which can have the (possibly changed) reference grabbed from it later.
In other words, if your method looks likes this:
public void doSomething(byte[] data)
{
for (int i=0; i < data.length; i++)
{
data[i] = (byte) i;
}
}
then you're fine. If your method looks like this:
public void createArray(byte[] data, int length)
{
// Eek! Change to parameter won't get seen by caller
data = new byte[length];
for (int i=0; i < data.length; i++)
{
data[i] = (byte) i;
}
}
then you need to change it to either:
public byte[] createArray(int length)
{
byte[] data = new byte[length];
for (int i=0; i < data.length; i++)
{
data[i] = (byte) i;
}
return data;
}
or:
public class Holder<T>
{
public T value; // Use a property in real code!
}
public void createArray(Holder<byte[]> holder, int length)
{
holder.value = new byte[length];
for (int i=0; i < length; i++)
{
holder.value[i] = (byte) i;
}
}
For more details, read Parameter passing in C# and Parameter passing in Java. (The former is better written than the latter, I'm afraid. One day I'll get round to doing an update.)
Actually, in Java, the references are passed-by-value.
In this case, the reference is a byte[]
object. Any changes that affect the object itself will be seen from the caller method.
However, if you try to replace the reference, for example using new byte[length]
, you are only replacing the reference that you obtained by pass-by-value, so you are not changing the reference in the caller method.
Here's an interesting read about this issue: Java is Pass-by-Value Dammit!
Here's an concrete example:
public class PassByValue
{
public static void modifyArray(byte[] array)
{
System.out.println("Method Entry: Length: " + array.length);
array = new byte[16];
System.out.println("Method Exit: Length: " + array.length);
}
public static void main(String[] args)
{
byte[] array = new byte[8];
System.out.println("Before Method: Length: " + array.length);
modifyArray(array);
System.out.println("After Method: Length: " + array.length);
}
}
This program will create a byte
array of length 8
in the main
method, which will call the modifyArray
method, where the a new byte
array of length 16
is created.
It may appear that by creating a new byte
array in the modifyArray
method, that the length of the byte
array upon returning to the main
method will be 16
, however, running this program reveals something different:
Before Method: Length: 8
Method Entry: Length: 8
Method Exit: Length: 16
After Method: Length: 8
The length of the byte
array upon returning from the modifyArray
method reverts to 8
instead of 16
.
Why is that?
That's because the main
method called the modifyArray
method and sent a copied reference to the new byte[8]
by using pass-by-value. Then, the modifyArray
method threw away the copied reference by creating a new byte[16]
. By the time we leave modifyArray
, the reference to the new byte[16]
is out of scope (and eventually will be garbage collected.) However, the main
method still has reference to the new byte[8]
as it only sent the copied reference and not an actual reference to the reference.
That should demonstrate that Java will pass reference using pass-by-value.
Java uses pass by value for method arguments.
- Primitives (int, boolean, etc.) are special cases in Java.. not objects per se. In this case, a copy of the primitive (argument) is passed into the function. This gels well with the pass by value theory.
- For Objects, what happens is that the ref to the object is passed by value (a copy of the reference is made rather than the object)... but both references point to the same object. So if you modify an object parameter in a method, the actual object will be modified.
This article should help you out.. http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html
As for the OP's question, just pass in the reference to the byte[] array to the method. The net result would be similar to pass by reference. If you modify the byte array, the caller will be able to see the changes post method execution.
Update to quell the resistance :) => indicates output
.NET Land
class Counter
{
private int m_count = 0;
public override string ToString()
{
return String.Format("Counter ID{0} : Value {1}", this.GetHashCode(), m_count);
}
public void Increment()
{ m_count++; }
}
class MakeAPass
{
public void PassByValueAndModify(int i)
{ i = 20; }
public void PassByRefAndModify(ref int i)
{ i = 20; }
public void PassByValueAndModify(Counter c)
{ c.Increment(); }
public void PassByRefAndModify(ref Counter c)
{ c.Increment(); }
public void PassByRefAndReassign(ref Counter c)
{
c = new Counter();
for (int i=0; i<5; ++i)
c.Increment();
}
}
static void Main(string[] args)
{
MakeAPass obj = new MakeAPass();
int intVal = 10;
obj.PassByValueAndModify(intVal);
Console.WriteLine(intVal); // => 10
obj.PassByRefAndModify(ref intVal);
Console.WriteLine(intVal); // => 20
Counter obCounter = new Counter();
obj.PassByValueAndModify(obCounter);
Console.WriteLine(obCounter.ToString()); // => Counter ID58225482 : Value 1
obj.PassByRefAndModify(ref obCounter);
Console.WriteLine(obCounter.ToString()); // => Counter ID58225482 : Value 2
obj.PassByRefAndReassign(ref obCounter);
Console.WriteLine(obCounter.ToString()); // => Counter ID54267293 : Value 5
}
Java Land
Minor mods reqd: Use hashCode() and + to concat strings in Counter.java...
class MakeAPass
{
public void PassByValueAndModify(int i)
{ i = 20; }
// can't be done.. Use Integer class which wraps primitive
//public void PassByRefAndModify(ref int i)
public void PassByValueAndModify(Counter c)
{ c.Increment(); }
// same as above. no ref keyword though
//public void PassByRefAndModify(ref Counter c)
// this can't be done as in .net
//public void PassByRefAndReassign(ref Counter c)
public void PassAndReassign(Counter c)
{
c = new Counter();
for (int i=0; i<5; ++i)
c.Increment();
}
}
public static void main(String args[])
{
MakeAPass obj = new MakeAPass();
int intVal = 10;
obj.PassByValueAndModify(intVal);
System.out.println(intVal); // => 10
//obj.PassByRefAndModify(ref intVal);
//System.out.println(intVal); // can't get it to say 20
Counter obCounter = new Counter();
obj.PassByValueAndModify(obCounter);
System.out.println(obCounter.ToString()); // => Counter ID3541984 : Value 1
//obj.PassByRefAndModify(ref obCounter);
//Console.WriteLine(obCounter.ToString()); // no ref. but can make it 2 by repeating prev call
obj.PassAndReassign(obCounter);
System.out.println(obCounter.ToString()); // => Counter ID3541984 : Value 1
// can't get it to say 5
}