I'm working with a program that uses two-dimensional arrays of Strings (probably not that smart to begin with, but eh), and I'd like to write a function that takes one of these arrays (let's say array1), makes an independent copy, and returns it (let's say array2). However, when I then change a value in array2, it seems to be reflected in array1.
My function currently looks something like this:
public static String[][] copy(String[][] matrix, int n) {
String[][] out = new String[n+1][n+1];
for (int i = 0; i < n+1; i++)
for (int j = 0; j < n+1; j++) {
if(matrix[i][j] != null) {
String cp = new String(matrix[i][j]);
out[i][j] = cp;
}
}
return out;
}
I declare a new array of Strings, and then iterate through it, copying each value individually. When that didn't work, I even tried explicitly declaring a new string from each old string and putting that in the array instead.
Can anyone tell me where I'm going wrong?
I'm not sure what the n parameter is for, but if I needed such a function, I'd use something like this:
public static String[][] copy(String[][] matrix) {
String[][] copy = new String[matrix.length];
for (int idx = 0; idx < matrix.length; ++idx)
copy[idx] = matrix[idx].clone();
return copy;
}
You don't need to create a copy of the String, because they are immutable. As pointed out by Michael in the comments, the String(String) constructor might be useful if the original string was created as a substring of some very large string. Another use is when you are using String objects as locks (not recommended), and want a private instance to avoid deadlocks.
Also, your check to see whether an element is null before assigning is unnecessary; if you have your loops setup correctly, the element is guaranteed to be null. (And if it's not, what's the harm in overwriting it?)
Your method looks like it should work, though passing in n as a parameter makes it brittle, using the input array's length field would be better, and you could even handle jagged arrays that way.
Making a copy of the contents is not necessary, since Strings cannot be changed - which leads to the main question: What kind of changes are you making that seem to be reflected in the copy? Show us the code that does this.
Have a look at System.arraycopy. That way you can get rid of the inner loop.
Maybe Arrays.copyOf would be of some use?
I tried with your code : got exception
java.lang.ArrayIndexOutOfBoundsException
It's working for me, please try like this :
public static String[][] copy(String[][] matrix, int n)
{
String[][] out = new String[n][n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++) {
if(matrix[i][j] != null) {
String cp = new String(matrix[i][j]);
out[i][j] = cp;
}
}
return out;
}
Take a look at this question Is Java pass by reference? It can be a little confusing how Java passes objects, but this would explain why making a change to one array also makes the change to your other array.
Your use of the 'n' parameter, as noted above is redundant, but also flawed by your code with n+1?? Your code would produce an ArrayIndexoutOfBoundsException if run something like:
String [][] m1 = { {"A", "B"}, {"C", "D" } };
String [][] m2 = copy(m1, 2);
Which presumably is how you intend it to be invoked?
It also limits your function to square 'matrices' of Strings.
But as for the problem you cited, I see no reason why the program should behave that way... I even ran it using the above call (but with n=1???) then changed
m2[0][1] = "X";
and m1 was unaffected as expected.
Even replacing the innermost code line to:
out[i][j] = matrix[i][j];
does not change this, as the compiler rewrites it to what you originally had. In fact a lot of the String sytax is simply syntactic sugar for StringBuffers (such as concatenation and assignment). eg the compiler will rewrite
String s = "Hello ";
s += "World"; // Makes it appear that String is builtin type!
to
String s = new String("Hello ");
s = new StringBuffer(s).append("World").toString();
Which is why is you have lots of string concatenation inside loops they can perform very poorly.
I do not understand why you are having the problem you cited either.
And as you are not modifying the parameter 'matrix', 'Pass By Reference' has nothing to do with it.
Related
I have a problem with a program I'm writing for a school assignment.
Essentially, before this piece of code, I already recieve and work with a bunch of information that I store into an array of objects. Now I have to sort this array (after it's sorted, I will have to calculate some things in the order of the PRIORITY variable).
presume I already have a MyClass[] array called input, that stores a finite amount of MyClass objects.
MyClass[] priorityArray = new MyClass[input.length];
for (int i=0; i<priorityArray.length; i++) {
int maxIndex = 0;
int maxPrivilege = input[i].returnPrivilege();
for (int j=1; j<input.legnth; j++) {
int currentPrivilege = input[j].returnPrivilege();
if (currentPrivilege > maxPrivilege) {
maxPrivilege = currentPrivilege;
maxIndex = j;
}
}
priorityArray[i] = input[maxIndex];
input[maxIndex].setPrivilege(-900000000);
}
the MyClass class if nothing fancy, but of course, contains a proper constructor, getter and setter methods and an integer variable "privilege".
I'm getting an error in my final tests of the program and, seeing as the program returns privileges as "-900000000", it has to have something to do with this part of the code.
It's also not even writing certain MyClass instances from the input array into the priorityArray array.
How can I clead this up? Help.
I'll rewrite my answer totally.
In this line
priorityArray[i] = input[maxIndex];
You are assigning object from one array to another array by reference. It means that there is only one object and you set value to -9000000 in the next line to it. Of course element in priorityArray will have the same changes. To fix it you need to clone your object here.
I've been trying to solve a problem that requires a couple of methods:
IT LOOKS LIKE A LOT, but you don't really need to read the methods. Just know that I'm passing an int[][] to a method, changing the int[][] inside the method, then checking for equality to an int[][] outside the method.
public static boolean One(int[][] matrix){
matrix = transpose(matrix);
matrix = reverseRow(matrix);
return Arrays.deepEquals(matrix, changed);
}
public static boolean Two(int[][] matrix){
//180 degree clockwise
matrix = transpose(matrix);
matrix = reverseRow(matrix);
matrix = transpose(matrix);
matrix = reverseRow(matrix);
return Arrays.deepEquals(matrix,changed);
}
public static int[][] transpose(int[][] matrix){
for(int i = 0; i < N; i++) {
for(int j = i+1; j < N; j++) {
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
return matrix;
}
public static int[][] reverseRow(int[][] matrix){
for(int j = 0; j < N; j++){
for(int i = 0; i < N / 2; i++) {
int temp = matrix[j][i];
matrix[j][i] = matrix[j][N - i - 1];
matrix[j][N - i - 1] = temp;
}
}
return matrix;
}
public static int[][] reverseCol(int[][] matrix){
for(int col = 0;col < matrix[0].length; col++){
for(int row = 0; row < matrix.length/2; row++) {
int temp = matrix[row][col];
matrix[row][col] = matrix[matrix.length - row - 1][col];
matrix[matrix.length - row - 1][col] = temp;
}
}
return matrix;
}
The structure of the problem is as follows:
I have an int[][] of original numbers
I have an int[][] of numbers, that is a result of a transformation of the original numbers. Specifically, a 90 degree clockwise turn, and an 180 degree clockwise turn.
The actual problem is much bigger, but I think there's a rule against getting straight up answers to problems, so I cut it down to what I need.
The problem is this:
After I run method one, the original array becomes modified, so it's corrupted before I run method two.
For instance,
Original before method One:
1111
0000
0000
0000
Original after method One;
0001
0001
0001
0001
These changes happens right after I run transpose(matrix) and reverseRow(matrix). Note that in java debug mode, I can see original being changed when I step over these methods. I DIDN'T EVEN MENTION ORIGINAL. I modified a passed of a passed version of Original.
So, 3 questions:
I thought that objects passed to methods don't get changed unless you return the changed object and then set the original to the changed in main() or the original method? I'm not talking about transpose, or reverseRow, because those were supposed to change matrix inside method One.
I tested this out with a method using strings. My belief above held true. Are int[][]'s different?
How can I fix it so that original doesn't get changed? Or am I missing something really simple?
This is easiest to understand when you learn that Java variables hold references, not objects.
And those references are always passed by value, as discussed in the earlier question Is Java "pass-by-reference" or "pass-by-value"? .
This answer addresses your specific questions.
1) I thought that objects passed to methods don't get changed unless you return the changed object and then set the original to the changed in main() or the original method?
Objects don't get passed to methods. Java variables are references to objects. You can copy a java variable without copying the object.
If you pass a variable of reference type to a method, that method can't modify your variable. It can, however, modify the object referenced by the variable, if it's mutable.
2)I tested this out with a method using strings. My belief above held true. Are int[][]'s different?
The array is a mutable object that you're accessing through a reference.
The String is an immutable object. You're probably trying to change the caller's variable by assigning to the called method's variable.
3)How can I fix it so that original doesn't get changed? Or am I missing something really simple?
You can make a copy of the array before passing a reference to it to a method.
One way to do that for a one-dimensional array is to use System.arraycopy(). Your two dimensional array is constructed from multiple one-dimensional arrays, so you could use multiple calls to System.arraycopy() to copy it.
Other approaches are discussed in this question: How do I copy a 2 Dimensional array in Java? . Note that the accepted answer in the question omits the necessary step of creating the two-dimensional array.
In Java everything is passed by value. However, the confusing thing is that this value holds a reference to the original object.
Assigning a variable only changes what reference the input variable holds:
void doSomething(String str){
str = "New String"; // str variable now holds a reference to "New String"
// however, the original String Object was not changed
}
However, this does not change the original string. In fact, String is an immutable Object, along with all primitives int, double, etc. This leads me to the following statement:
The only way to change an input parameter of a calling function is to call a method of that parameter that mutates the object (e.g. param.setValue(newValue) or param[0] = newValue).
This allows us to make a couple observations:
Primitive types (char, int, double, etc.) can never be changed in a calling function because they don't contain any methods to mutate the data.
Immutable Objects (String, Integer, Double, etc.) can never be changed in a function, because by definition they do not contain methods that mutate the data.
Mutable Objects (StringBuilder, BigInteger, BigDecimal, etc.) can only change by calling mutator methods on the object (not by reassigning the variable). For arrays, you can think of array[0] = newValue, etc., as mutator methods on an Array akin to list.set(0, newValue) for a List.
I have to create a few variables like: "n23, n4, n18...".
So, they consist of two parts: letter "n" and a number witch I want to get from my method for generating random numbers(rand()).
Something like this:
for(int i = 0; i < 6; i++) {
int n*here_must_be_random_number_got_from_my_method*;
}
Is it possible to do something similar?
Java is no script language.
All identifiers, which means all package names, type names, method names, field names and variables, must be specified at compile time. So there is no way to concatenate your variable names based on values calculated at runtime.
BUT!
You do not even need to. Just use a java.util.Map. Instead of doing
int n*here_must_be_random_number_got_from_my_method* = *whatever_it_is_you_want_to_put_here*;
you can instead do
Map<Integer, Integer> myMap = new HashMap<>();
once, then put values inside the map like so:
myMap.put(*here_must_be_random_number_got_from_my_method*, *whatever_it_is_you_want_to_put_here*);
and get it back like so:
myMap.get(*here_must_be_random_number_got_from_my_method*);
You can't, but presumably you want to refer to these later, and if you were intending to refer to n123, then you can hopefully refer to them as n[123] instead. If that's the case, then you can use a java array, perhaps like this:
private int[] n = new int[999];
void populate() {
for(int i = 0; i < 6; i++) {
n[here_must_be_random_number_got_from_my_method] = something;
}
}
Note that java doesn't do sparse arrays, so the size of your array needs to be as large as the largest random number. If this us large, consider using a map as per Jan's answer.
I want to create a method along the lines of [return] methodName(int numberOfTimes, String[numberOfTimes] strings), meaning that, depending on the value of numberOfChoices, I can add that number of String values to the method.
I've wondered and, as I wrote it, it wouldn't work because it would fail at compile time, because numberOfChoices isn't defined, and, if it would compile, it'd still be tricky to work it out.
I think my best bet is going with String... strings and do a for loop like this:
void methodName(int numberOfTimes, String... strings) {
for(int i = 0, i < numberOfTimes; i++) {
// do something with strings[i]
}
}
But I still wonder if what I originally wanted was possible.
EDIT: I'm dumb and am always thinking on "sharing" my methods outside of the workspace, that's why I want it to work on the most general way possible. Solution is actually removing numberOfChoices and just introducing as many String objects as needed in methodName. Something along the lines of methodname("One", "Two"). So, fixed code would be...
void methodName(String... choices) {
for(int i = 0; i < choices.length; i++) {
// do something with choices[i]
}
}
if the variable parameter is always of the same type (like strings) you could use an array or list as a parameter, the method won't care what the size of the array is, just that it's an array
void methodName(int numberOfTimes, String[] myStrings)
The three periods after the final parameter's type indicate that the final argument may be passed as an array or as a sequence of String arguments. You could then use loop without knowing the number like:
for (String stringParam: args) {
// your logic on stringParam
}
In java arrays are dynamic so their is no need to specify index within method argument
[return] methodname(int numberOfTimes, String[] strings)
it works
Java treats variable arguments as arrays, so you can iterate over strings like normal.
Example (returning all the strings concatenated with spaces):
public void wordsToSentence(String... words) {
final StringBuilder builder = new StringBuilder();
boolean notFirst = false;
for (String s : words) {
if (notFirst) builder.append(" ");
else notFirst = true;
builder.append(s);
}
return builder.toString();
}
I've an array of objects in Java. Say these objects Obj contain a variable var, and I have several Obj stored in an array Array[].
I'd like to compare the var between two adjacent Obj but I don't know how, nor can I find any info online (which makes me think i'm working my question wrong or it's not doable or something)
Edit:
I'm currently attempting the rather roundabout method of assigning the array objects in question to new temporary objects and just doing the comparison with those:
Obj o1 = o[i];
Obj o2 = o[i+1];
if (o1.var > o2.var)
//etc
But surely there is something better.
If you have an array of objects, you can do your comparison without creating the temporary references:
MyObject[] arr = //populated somehow
for (int index = 0; index < arr.length - 1; index++) {
if (arr[index].var > arr[index + 1].var) {
//your logic
}
}
You might also want to take a look at the Comparable interface as a means of encapsulating the comparison of the objects based on a particular field. Using this interface would allow you to take advantage of its support in the Collections API.
Based on your edit, it would be fine to say
if (o[i].var > o[i+1].var) { ... }
assuming that o was of type Obj[].
I'm curious, though: are you trying to sort the array? If so, you can use Arrays.sort() (If not, it's a good method to know about anyway.)
I may not be understanding your question correctly, but the following is perfectly valid:
if (o[i].var > o[i+1].var ) { // ...etc... }
Beware of when you hit the end of the array! That is, if you are looping through all of the elements and i is the last one, then o[i+1] will give you an Array Index Out of Bounds error!
Just use them directly without the reference i.e. substitute the array lookup into where you are doing the comparison.
if (o[i].var > o[i+1].var) {
// etc
}
Or in a loop, doing every one programmatically:
for (int i=0; i<o.length-1; i++) {
if (o[i].var > o[i + 1].var) {
// etc
}
}