Java Regarding Object Scope and Modification on the Object - java

I have a question regarding I guess the scope of objects and whether or not the values the object holds would be changed under certain conditions.
For example, I have made a class in which I need to contruct more than 1 instance of the object, but these instances need to be used and modified throughout the programm and no new instances should be made after the first are made (I don't know what design pattern this would be following if any). The way I have designed the object is something similar to the following:
//basic constructor class
public class MyObject {
private String var1;
private int var2;
private boolean vr3
public MyObject(String param1, int param2, boolean param3) {
var1 = param1;
var2 = param2;
var3 = param3;
}
//getter and setter methods
}
//in main class
Map<String, MyObject> myObjects = new HashMap<String, MyObject>();
on the start of my program, I search through some files to construct new MyObject objects then put them in the HashMap and that is the only time a new MyObject should be created. So throughout the program, I get the objects in the HashMap by getting the value that matches the String key, and once I have that object, I may do things to it using the different setter methods like below:
MyObject object1 = MyObjects.get("anObject");
object1.setVar1("This is the objects new var1 string value");
And that code above should change the string value in the object that is in the HashMap under the key "anObject". But I am wondering that should this also work for things like Lists? like say I had a list as one of the values in the object, and if I got that object from the HashMap, and called something like:
object1.getList().add("new value in the object1 list");
would that add the value to the object in the hashMap? I am wondering this because since I called:
MyObject object1 = MyObjects.get("anObject");
it seems like it could be creating a new instance of that class or just copying it over and that any changes to that object1 object wont be made to the object in the HashMap.
Am I correct that the changes made to any values to the object gotten form the HashMap will be put back to the object in the HashMap?
Sorry for this stupid question.

Java is pass by value, however MyObject is a reference and this is passed by value.
This means every copy of a reference to your Object is the same object and wherever you attempt to change it, the same object will be changed.
The object itself will only be copied when you copy it, not implicitly.

You are correct: any changes made to the internals of the object that you retrieve from the HashMap will be changed in the HashMap, as when you retrieve an object from any data structure, you are actually getting a pointer to an object, so the HashMap and the pointer you get back both point to the same object data. The only times when this is different is if you are storing primitives (like int, double, float, etc.) or if you reconstruct the object you have received from the HashMap.

Related

ArrayList modifying value returned by "get" method [duplicate]

This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 6 years ago.
I have below two situations related to ArrayList get method, one with custom class and one with String class:
1. Below is the example of modifying Custom class ArrayList element:
ArrayList<MyClass> mTmpArray1 = new ArrayList<MyClass>();
MyClass myObj1 = new MyClass(10);
mTmpArray1.add(myObj1);
MyClass myObj2 = mTmpArray1.get(0);
myObj2.myInt = 20;
MyClass myObj3 = mTmpArray1.get(0);
Log.d(TAG, "Int Value:"+myObj3.myInt); // Prints "20"
2. And below is the example of modifying String ArrayList element:
ArrayList<String> mTmpArray2 = new ArrayList<String>();
mTmpArray2.add("Test_10");
String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";
String myStr2 = mTmpArray2.get(0);
Log.d(TAG, "Str Value:"+myStr2); // Prints "Test_10"
So in case of MyClass ArrayList, when I call get and modify the value, then I see change is reflecting when I do get again.
But same way when I modify String ArrayList, then changes are not reflecting.
What is the different in of the get method in both the scenarios?
Is it that in case of String, String class creating deep copy and returns new object, and in case of Custom class shallow copy is created?
In the first scenario applicable to "LinkedHashMap", "HashMap" and "List"?
Your are not doing the same thing in the two cases.
Here you update the state of an object, so the change affects the object stored in the list :
myObj2.myInt = 20;
Here you are assigning a new object to a local variable, so the list is not affected :
myStr1 = "Test_20";
If String was mutable, you could have modified the String by calling some method, and the change would have been reflected in the object stored in the list :
myStr1.setSomething(...);
On the other hand, if in the first case you would have changed the value of the local variable, the object stored in the list wouldn't have been affected :
myObj2 = new MyClass (...);
Strings are immutable. You're not inserting the new string into the array list.
When you do String myStr2 = mTmpArray2.get(0);, even tho you are pointing to a reference in the ArrayList, any attempt to change the value, will (because of String immutability) create a new String (myStr2) that will not reference the ArrayList anymore.
When you do myStr1 = "xxx", you're not actually changing the ArrayList reference, you're changing a new (copy) (now called myStr1) that was grabbed from the ArrayList and it has a local scope.
Read some more about Strings: Immutability of Strings in Java
Now in the first example, you are pointing to a mutable object (your custom class) so you're literally changing the direct value, through the reference. Welcome to Java. ;)
Unrelated: This code: MyClass myObj1 = new MyClass(10); is (arguably) considered bad. It's better to use a factory pattern that is a lot easier to read. In other words, public constructors with parameters are hard to read (for example, I have no idea what I am constructing when I read your code).
A (perhaps) better approach would be: MyClass myObj = MyClass.createInstanceWithCapacity(10); // i've invented the name because I don't know what your 10 is, but look at both, which one do you think is easier to understand upon first glance?
Disclaimer: The above unrelated comment is my personal opinion and not all developers will agree. ;)
Strings have very nice property called "Immutablity"
This means that String cannot be mutable (changed), when we create/
try to refer to old string, a new instance string is created. And any
changes we do are saved in new instance and it do not affect the old
string
For example,
String s = "Old String";
System.out.println("Old String : "+s); // output : Old String
String s2 = s;
s2 = s2.concat(" made New");
System.out.println("New String : "+s2); // output : Old String made New
System.out.println("Old String is not changed : "+s); // output : Old String
These is no difference between the two "get" calls. The difference is between the types that the ArrayList is holding, and what you're doing the references the "get" method returns.
In your first example, you do this:
MyClass myObj2 = mTmpArray1.get(0);
myObj2.myInt = 20;
Here, you're getting a reference to the MyClass instance in the ArrayList in position 0, and you are modifying a field within this instance.
In your second example, you do this:
String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";
Here, you're getting a reference to the String instance in the array list, and then you're giving myStr1 a reference to a different string which you create ("Test_20"). It's as if you did wrote myObj2 = new MyClass(20); in the 2nd line in the 1st example.
So, in short, in the 1st example, you access a field within the object by altering it from the reference you grabbed. In the 2nd example, you simply altered your reference to point at a different String.
I should also mention that in Java, Strings are immutable, meaning once they have been created, they cannot be changed.
String is an immutable class. A line like
myStr1 = "Test_20";
does not change the value of the String object myStr1 is pointing to. Instead a new String is created and myStr1 is modified to point to the new String. The original String is unchanged and can be retrieved from the ArrayList.
Your MyClass object is clearly mutable. Only one instance is created and its state is changed by the assignment
myObj2.myInt = 20;
Hence when this object is retrieved from the ArrayList, its new state is seen.
You simply do NOT change the list in your 2nd example.
In the first example, you are doing this:
Get the first object from the list and store it in the variable called 'myObj2'
Modify the object stored in variable 'myObj2' by setting the int value of this object
But your second example is completely different:
String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";
Let me translate that:
Get the first element from the list and store it in the variable called 'myStr1'.
Set the value of the variable 'myStr1' to "Test_20"
So, in case one you are modifying a variable of the object stored in the list. In case two you are retrieving the object stored in the list - and then re-use the variable you stored that retrieved object in and use it for something new - but that does not change the original list, of course.
To modify your list for a type like string, you would need to use set(x, "Test_20").

Get two different class instances and compare them (Java)

I am receiving input from my Ext JS application on the front end and I want to compare two records on the java side, the record prior to a form update on the UI side and the record after setting all the new values. I have made two references to the same type of object, one before setting new values (oldRecord) and one after (newRecord) and compare these but when I try to use oldRecord.getClass() on the old record it will show the new field values. Is there any way to have access to the old values? I also tried making two field arrays and passing those into my method.
private static Map<String, List<String>> changes;
public static <T> Map<String, List<String>> getChanges (T oldRecord, T newRecord) {
changes = new HashMap<String, List<String>>();
try {
if(oldRecord instanceof UserInfo && oldRecord.getClass().equals(newRecord.getClass())) {
Field field = oldRecord.getClass().getDeclaredField("name");
field.setAccessible(true);
System.out.println(field.get(oldRecord));
field = newRecord.getClass().getDeclaredField("name");
field.setAccessible(true);
System.out.println(field.get(newRecord));
// will loop through all fields in both records and compare
}
} catch (Exception e) {
System.err.println("Exception");
}
return changes;
}
I think you are using the same object. This is the case if you have done something like this:
Object oldRecord = new Object();
Object newRecord = oldRecord;
newRecord.doSomethingThatModifyIt();
getChanges(oldRecord, newRecord);
If you have done something like this, then keep in mind that Object newRecord = oldRecord; does not create any new object, but just sets the 2 variables to point to the same object.
You should instead create a new object based on the old one, with some method like clone() that you would have to implement. Or just by manually setting the fields of the newly created object.
Note: I have used Object but it could be any type of course.
If oldRecord and newRecord reference the same object like
T oldRecord = new T();
T newRecord = oldRecord;
they will "hold" the same instance variable values as well.
To save in oldRecord the old values you must do a deep copy of the object, that means create a new object and copy the instance variable values.
SEarch for deep copy or cloning, maybe start here: How do you make a deep copy of an object in Java?

How to change a value of a field in an ArrayList of objects?

I know how to add an object:
ArrayList<Object> ob = new ArrayList<Object>();
ob.add (index, some_object);
But let say the object has a field called 'name', how can I change that single field only?
For example:
ob.setName(name);
(I know this does not work.)
There's no special treatment here:
ArrayList<SomeObjectType> ob = new ArrayList<SomeObjectType>();
//...
ob.add(index, some_object);
some_object.setName(name);
The object you wanted to set name for is some_object, not ob.
If you are asking about a situation where you have a List which contains the object you want to update, but you don't yet have a reference to that object, then you will need to first find the object within the list, and then update its name field.
If you do have the reference to the object, then the fact that it is contained in a list is irrelevant: call some_object.setName(name) and the object will have the new name whether it is fetched from the list or directly.
I think maybe you want to see
public void stuff(List<? extends Foo> list, int indexToAlter){
// Get the item in the list at the index and call the appropriate method
list.get(indexToAlter).methodOnFoo();
}
You seem to be confusing the list you are storing your object in and the object itself. Try something like:
Object temp = ob.get(index);
temp.name = new_name;

Immutable Class how to tell

I understand that immutable means that it is an object that will not change state after it is instantiated. But in this line of code I dont see Final when the array values is declared.
Is this class immutable? Can anyone explain how to find out. Thanks
public class A {
private double[] values;
public double[] getValues(){
return values;
}
}
As other have written this object is considered to be mutable in its state. What it is immutable to is that you can not exchange the array it holds. But you can change the array's content (getValues()[0] = 10;).
To convert this to a immutable object you must use List instead of an array. With List you can use Collections' method unmodifiableList to convert a given list into a version you can savely expose to the outside. If the caller of getValues() uses add or remove on a unmodifiable list it will result into a UnsupportedOpertionException keeping your object save from being modified.
If you need to stick to arrays you need to provide a copy (System.arraycopy) or a clone (clone()) of the array.
Usually a object is considered to be immutable if you can not change its properties (including inherited properties from superclasses. This usually includes the properties values as well but this is a blurred definition.
For example if you have a class that holds a File instance which points to document file and this File instance can not be changed the class is considered to be immutable (the inforamtion it provides never changes) but the document it points to can be mutated and changed every time. So its a blurred line actually (remember in your example you can not change the array but the content of the array).
Yes the code pasted is not having any final keyword associated and has no immutable behavior.
Well i would like to bring forth some key guidelines related to writing immutable classes in java :
1.) Ensure the class cannot be overridden - make the class final, or use static factories and keep constructors private
2.) Make fields private and final
force callers to construct an object completely in a single step, instead of using a no-argument constructor combined with subsequent calls to setXXX methods (that is, avoid the Java Beans convention)
3.) Do not provide any methods which can change the state of the object in any way - not just setXXX methods, but any method which can change state
4.) If the class has any mutable object fields, then they must be defensively copied when they pass between the class and its caller
A a = new A();
a.getValues()[0] = 1.2;
This would work as long as values is not empty. You will however not be able to reassign values to a new array. That is: a.getValues() = new double[5]; will not work.
The class is not immutable, as I can change values, just not reassign it.
Here is a simple verification. the values are initialized to 1,2.
Using the getter and a reference, one is able to change the values inside the first item in the array after the object is created
public class A {
private double[] values;
public double[] getValues() {
return values;
}
public static void main(String[] args) {
A test = new A();
test.values= new double[]{1, 2};
double[] valuesref = test.getValues();
valuesref[0] = 10;
for (int i = 0; i < test.values.length; i++) {
System.out.println(test.values[i]);
}
}
}
This can be avoided if getValues() returns a copy of the array.

Add additional info to arraylist

What is the best way to store one single additional attribute at an arraylist?
Let's say I pass an ArrayList of Objects to a method and get an ArrayList back with an additional flag.
ArrayList<MyObject> list = new ArrayList<MyObject>();
//fill
list = myMethod(list);
And I want to get a boolean for each Object, but I don't want to store it directly in the object.
So data before myMethod: Apple, Banana, Grapefruit
After myMethod: Apple=>true, Banana=>false, Grapefruit=>false.
How can I store that additional attribute when not in the object? What is the best datatype? ArrayList can not do that, right?
You can create a custom wrapper around Object that has a field of type object and a boolean field. This is the best solution as you will be able to choose a meaningful name for the field.
Alternatively you can use the built-in class Pair and create a List<Pair<MyObject, Bool>>.
Use a class with a List<MyObject> field and a List<Boolean> field whose elements are flags parallel to every MyObject added in your List<MyObject>. Since this is too cumbersome, just add the boolean flag in MyObject. If you can't modify the class, extend it and add the boolean flag in your ExtendedMyObject extends MyObject class. If you can't extend it, make a wrapper class of MyObject that has a MyObject field and the boolean flag.
You could separately maintain a Set<MyObject> - membership in the Set would correspond to a true value for that object.
Assuming you used a HashSet, you would need to ensure MyObject implemented equals and hashCode. An alternative would be uniquely identifying each MyObject with a String, Long, etc. and maintaining a Set of those IDs.
I'd recommend just returning a Map with the source object (or name if unique) mapped to the boolean value.
Map<MyObject,Boolean> result = myMethod(list);
or, if name is unique:
Map<String,Boolean> result = myMethod(list);
for (MyObject item : list) {
Boolean result = result.get(item.getName());
if (result) {
...
}
}
If:
MyObject doesn't implement #hashCode (so you may get missed hashes for equal objects)
name isn't unique or
if result were more complex than a Boolean,
then I'd create a wrapper that wraps a MyObject with a result and return a List<MyObjectResult>.
Edit: BTW, I think you're right not to include the Boolean field on MyObject if it is not really a part of that object's state - i.e. if it's a transient result of some operation.
you can extend the ArrayList and give them a new property. For example:
public class MyArrayList<E> extends ArrayList<E>{
private boolean value;
public boolean isValue() {
return value;
}
public void setValue(boolean value) {
this.value = value;
}
}
there you can have all the features from the ArrayList and you have an additinal boolean field.
EDIT:
If your Boolean value has nothing to do with the Originalobject, why not just write a Wrapperclass? There are 2 possibilities to resolve this:
extend your Baseclass MyObject
Write a new class with 2 properties, one for the boolean and the other your instance of MyObject

Categories