immutableList but still able to modify elements in the list - java

I am using com.google.common.collect.ImmutableList to not allow any modification to the element in the list. The code looks like below but the assertion failed.
MyObj doesn't override clone method, is that why it fails?
MyObj myObj = new MyObj();
myObj.setName("foo");
Collection<MyObj> sets = new HashSet<MyObj>();
sets.add(myObj);
ImmutableCollection<MyObj> immutableSets = ImmutableList.copyOf(sets);
for(MyObj obj : immutableSets){
obj.setName("var");
}
assertTrue(myObj.getName()=="foo");

You are modifying an object inside the list, not the list itself.
If you want to avoid such modifications, your immutable list must contain immutable objects.

Related

Making a new ArrayList containing objects the same as the first list?

I have an ArrayList containing custom objects.
What is the best way to make a separate ArrayList that has the exact same content, but isn't using the same references? As in, if I edit the first object in list1, it doesn't touch the first object in list2, but otherwise they look the same through and through.
Is it considered correct / good practice to do the following, or is there a built-in way?
List<MyObject> firstList = getArrayListFromSQLiteDb(criteria);
List<MyObject> secondList = new ArrayList<>();
for (MyObject object : firstList) {
MyObject newObject = new MyObject();
newObject.setField1(object.getField1());
newObject.setField2(object.getField2());
newObject.setField3(object.getField3());
secondList.add(newObject);
}
A simple way of doing this would be to clone the original ArrayList, thus not sharing references and having the other list remain untouched when you alter the original one. As #911DidBush mentioned, this will only work if the lists contents are cloneable and implement the clone() method correctly.
List<MyObject> firstList = getArrayListFromSQLiteDb(criteria);
List<MyObject> secondList = new ArrayList<>();
for(MyObject obj : firstList) {
secondList.add(obj.clone());
}

ConcurrentModificationException at for each android

I am passing Arraylist of ParseObject, and then i am putting one one foreach loop to extract the items with a condition when user object is not equals to null. There are two problems which i am facing.
1. If i am doing the following lines of code by passing different data to another list and then pass that list in my adapter, i am getting random data with numbers for example: If on item # 1 the name is "MAC" then it is showing in item 3.
ArrayList<ParseObject> checkRequestedNetArrayList = new ArrayList<ParseObject>();
requestedNetArrayList = (ArrayList<ParseObject>) objects;
MyResponsibilitesActivity.requestedNetArrayList = requestedNetArrayList;
adapterRequest = new GenericAdapter<ParseObject>(
getApplicationContext(),
requestedNetArrayList,
R.layout.requested_trust_net_list_item,
requestedDataBinder);
requestListView.setAdapter(adapterRequest);
requestedNetArrayList = (ArrayList<ParseObject>) objects;
for(ParseObject object: objects){
System.out.println(object);
object.getParseObject("user");
if(object.has("user")){
checkRequestedNetArrayList.add(object);
}else{
checkRequestedNetArrayList.remove(object);
}
}
adapterRequest = new GenericAdapter<ParseObject>(
getApplicationContext(),
checkRequestedNetArrayList,
R.layout.requested_trust_net_list_item,
requestedDataBinder);
requestListView.setAdapter(adapterRequest);
If i am doing the following line of code to just direct giving the items in the same list, i am getting the java.util.ConcurrentModificationException
for(ParseObject object: objects){
if(object.has("user")){
requestedNetArrayList.add(object);
}
}
else{
requestedNetArrayList.remove(object);
}
adapterRequest = new GenericAdapter<ParseObject>(
getApplicationContext(),
requestedNetArrayList,
R.layout.requested_trust_net_list_item,
requestedDataBinder);
requestListView.setAdapter(adapterRequest);
}
Please help me out here.
You can not remove an element from list while accessing it.
You have to use Iterator.
Where ever you are removing the object, use it.remove();
Iterator<ParseObject> it = objects.iterator();
while(it.hasNext()){
Object object = it.next();
//your stuff
it.remove();
}
I think you might want to check this article about deep copy also.
UPDATE
Since you want to add elements to the list it is not directly possible with iterator. Now you are facing problem because you are directly assigning objects to requestedNetArrayList instead of that do it in the following way :
ArrayList<ParseObject> requestedNetArrayList = new ArrayList<>(objects);
Then iterate over objects as you are doing now, and remove from or add to
requestedNetArrayList (which you are pretty much already doing).
When you make iteration using for-each construction for Collection
for (Object x : collection) ...
you have implicit creation of Iterator object for that Collection. This iterator performs a check: is collection was changed since iterator was created? If so, throwing an exception. So, you should avoid to any modify to your collection, until iterator done. That means, you should not use add and remove.
In either way, it is better to access ArrayList by index, because it will prevent creation of Iterator object. Like this:
for (int i = objects.size() - 1; i >= 0; i--) {
ParseObject object = objects.get(i);
// when iterating from tail to head, you can safely add or remove objects to/from this array list
}
Instead of assigning the reference of objects to requestedNetArrayList,
create a new ArrayList with the same contents
requestedNetArrayList=new ArrayList<ParseObject>(objects);
Then you can iterate on objects and modify requestedNetArrayList.

How to create a copy of ArrayList<ArrayList<Arc>> to another ArrayList<ArrayList<Arc>>

I have a Class
public class Arc{
...
}
and 2 ArrayList
ArrayList<ArrayList<Arc>> rotalar1 = new ArrayList<ArrayList<Arc>>();
ArrayList<ArrayList<Arc>> rotalar2 = new ArrayList<ArrayList<Arc>>();
i try to copy rotalar1 to in rotalar2:
rotalar2.addAll(rotalar1);
but i have a problem. if i make any change in rotalar2 , it has an impact on rotalar1 too. I dont want to make a change in rotalar1 :
These rows make problem
rotalar2.get(random1).remove(random3);
rotalar2.get(random2).remove(random4);
Thanks for your time
Iterate over rotalar1 an copy each list of this list to rotalar2. You can make the copy in different ways. In the first example i use the constructor and create a new list with the same elements. Be careful, if you make changes to the Arc objects, this changes will still take effect in both lists. If you dont want this, you have to copy your Arc objects too.
ArrayList<ArrayList<Arc>> rotalar1 = new ArrayList<ArrayList<Arc>>();
ArrayList<ArrayList<Arc>> rotalar2 = new ArrayList<ArrayList<Arc>>();
for(ArrayList<Arc> list : rotalar1)
{
rotalar2.add(new ArrayList<Arc>(list));
}
This is another way, using Collections.copy(dest,src)
for(ArrayList<Arc> list : rotalar1)
{
ArrayList<Arc> copy = new ArrayList<>();
Collections.copy(copy, list); //or copy.addAll(list);
rotalar2.add(copy);
}
This problem is soved now:
rotalar2.get(random1).remove(random3);
rotalar2.get(random2).remove(random4);
but this will still take effect on both lists:
rotalar2.get(rndList).get(rndArc).set(xvy)
If you want to fix this problem too, you can do something like this:
ArrayList<ArrayList<Arc>> rotalar1 = new ArrayList<ArrayList<Arc>>();
ArrayList<ArrayList<Arc>> rotalar2 = new ArrayList<ArrayList<Arc>>();
for(ArrayList<Arc> list : rotalar1)
{
ArrayList<Arc> tmpList = new ArrayList<>();
for(Arc arcObj : list)
{
tmpList.add(copyOfyourArc); //TODO how do you want to creat a copy of Arc obj?
}
rotalar2.add(tmpList);
}
Method addAll does shallow copy which means it copies all the reference of object (not actual Object) from rotalar1 to rotalar2.
One way, you need to iterate over each object(Arc) and clone
it and add it to new list.
One other way is deep copy using serialization. Example Code
You are not deep copying your list.
The problem is, that your lists contain references to objects. If you copy that (list of) refereces to your second list, both references (the original and the copied one) are pointing to the very same object. Every change applied to the object through the referenc in the second list, will be visible in the first list as well.
To deep copy the collection, you could e.g. iterate over it and manually copy each index.
You should implement your own method to do a deep copy within the Arc class. The method should create a new instance of the object and set all fields(attributes) to the value that the current object has. This method should return a new Object of type Arc that has all the same values as the object trying to be copied. You should then iterate over your arraylist(s) and use your copy method on each object and add the new object to a new arraylist which will be a copy of your original arraylist. Example:
//I don't know the attributes of your Arc class this is just an example
public Arc deepCopy()
{
Arc copyOfArc = new Arc(this.field, this.field2, this.field3, this.field4, etc...);
//if you don't set everything in the constructor then you should use your setters here.
copyOfArc.setField5(this.Field5);
etc...
return copyOfArc;
}
Use this method to iterate over your arraylist and copy each arc object to a new arraylist.
Here is an example of using the deepCopy() method to copy your objects to new arraylist:
ArrayList<ArrayList<Arc>> rotalar2 = new ArrayList<ArrayList<Arc>>();
int counter = 0;
for(ArrayList<Arc> temp : rotalar1)
{
rotalar2.add(new ArrayList<Arc>());
for(Arc temp1 : temp)
{
rotalar2.get(counter).add(temp1.deepCopy());
}
counter++;
}
This will copy arraylist rotalar1 to arraylist rotalar2 with a deepCopy so each arraylist will have its own objects and won't effect each other anymore.

How to copy java.util.list Collection

I am implementing a Java class responsible for ordering java.util.List. The problem comes when I use this class. I'm able to ordering the list but I want to copy the "original" list without modification so that I could register every change made on the original list. The sorted list contains object and one of its fields stores a classification id, and this id it is updated with the index value of the list.
I tried to use clone method and it keeps the list unsorted but the changes made on original list are updated in the clone list too.
Is there any way to accomplish it?
My Code:
List<Torero> listaTorero = tbTlgTorerolHome.findByExample(new Torero());
List<Torero> listaToreroTemp = ((List<Torero>) ((ArrayList<Torero>) listaTorero).clone());
Clasificacion clasificacion = new Clasificacion();
Iterator<Torero> iterTorero = clasificacion.getClasificacion(listaTorero, torero).iterator(); //Sorting List
A Clasificacion method:
public List<Torero> getClasificacion(List<Torero> listaToreroTemp, Torero torero)
{
List<Torero> listaTorero = new ArrayList<Torero>();
Collections.sort(listaToreroTemp,new ToreroClasifiacionComparator());
Iterator<Torero> iterTorero = listaToreroTemp.iterator();
int index=1;
while(iterTorero.hasNext())
{
Torero toreroTemp = iterTorero.next();
toreroTemp.setNumClasificacion(index);
listaTorero.add(toreroTemp);
index=index+1;
}
return listaTorero;
}
You may create a new list with an input of a previous list like so:
List one = new ArrayList()
//... add data, sort, etc
List two = new ArrayList(one);
This will allow you to modify the order or what elemtents are contained independent of the first list.
Keep in mind that the two lists will contain the same objects though, so if you modify an object in List two, the same object will be modified in list one.
example:
MyObject value1 = one.get(0);
MyObject value2 = two.get(0);
value1 == value2 //true
value1.setName("hello");
value2.getName(); //returns "hello"
Edit
To avoid this you need a deep copy of each element in the list like so:
List<Torero> one = new ArrayList<Torero>();
//add elements
List<Torero> two = new Arraylist<Torero>();
for(Torero t : one){
Torero copy = deepCopy(t);
two.add(copy);
}
with copy like the following:
public Torero deepCopy(Torero input){
Torero copy = new Torero();
copy.setValue(input.getValue());//.. copy primitives, deep copy objects again
return copy;
}
Use the ArrayList copy constructor, then sort that.
List oldList;
List newList = new ArrayList(oldList);
Collections.sort(newList);
After making the copy, any changes to newList do not affect oldList.
Note however that only the references are copied, so the two lists share the same objects, so changes made to elements of one list affect the elements of the other.

What's the best way to detach a Collection from a Map in Java?

I obtain a HashSet from a HashMap and I don't want that my modifications on the HashSet reflect on the HashMap values.
What's the best way of doing something like this :
HashSet<Object> hashset = new HashSet((Collection<Object>) hashmap.values());
//Something like ...
hashset.detach();
//Then i can modify the HashSet without modifying the HashMap values
Edit :
I have to modify an element in the HashSet but I don't want to modify this same element in the HashMap.
Thanks!!!
If you're creating a new HashSet as per the first line of your code snippet, that's already a separate collection. Adding or removing items from the set won't change your hashMap. Modifying the existing items will, of course - but that's a different matter, and will almost always be a Very Bad Thing (assuming your modifications affect object equality).
When you create the HashSet from hashMap.values() like this, then it's already "detached" in the sense that modifying the HashSet will not influence the map it was constructed from.
However, if you modify an object inside the set (for example calling a setter on it), then those changes will be reflected inside the HashMap as well (since the Set and the Map will refer to the same object).
One way around this is to make defensive copies of each element (using clone() or by using a copy constructor).
Another way is to use immutable objects.
You are close:
Set<Object> set = hashmap.values(); // is backed by the map
// create a new hashset seeded from the other set
Set<Object> hashset = new HashSet<Object>(set);
If you are trying to copy the values, and change the state of the values you need to create a deep copy, which relies on knowing how to create copies of the objects held in the Map as values. Hopefuly this test illustrates what I mean.
#Test
public void testHashMap() throws Exception {
final Map<Integer, TestContainer<Double>> hashmap = new HashMap<Integer, TestContainer<Double>>();
final TestContainer<Double> t1 = new TestContainer<Double>(1d);
final TestContainer<Double> t2 = new TestContainer<Double>(2d);
hashmap.put(1, t1);
hashmap.put(2, t2);
// create a separate collection which can be modified
final Set<TestContainer<Double>> hashset = new HashSet<TestContainer<Double>>(hashmap.values());
assertEquals(2, hashmap.size());
assertEquals(2, hashset.size());
hashset.remove(t2);
assertEquals(2, hashmap.size());
assertEquals(1, hashset.size());
// prove that we cannot modify the contents of the collection
hashset.iterator().next().o += 1;
assertEquals(2d, t1.o, 0d);
}
private static final class TestContainer<T> {
private T o;
private TestContainer(final T o) {
this.o = o;
}
}
Try this:
public MyType cloneObject(MyType o) {
MyType clone = new MyType();
// TODO copy the attributes of 'o' to 'clone' return the clone
return clone;
}
public void populateHashSet(HashMap<Object,MyType> hashMap) {
HashSet<MyType> hashSet = new HashSet<MyType>();
for (MyType o : hashMap.values()) {
hashSet.add(cloneObject(o));
}
}
That said, I would be very careful about making copies of objects unless all the attributes of the object are primitive/immutable types. If you just copy an attribute object reference to an object reference in the clone then your 'clone' can still produce side-effects in the original object by changing the objects it references.

Categories

Resources