I got a question about constructing a new ArrayList based on an already existing list, I need it to reverse a List. I do not need to deep clone the elements, since I only inspect their values and do not change them.
My old, working, code, it seems a bit hackish to me, so I think I had problems with it in the past:
Collections.sort(invoiceWordGroups, new WordGroup.WordGroupComparator());
insertAttributes(topAttributeWords, invoiceWordGroups, templateId, templateAttributeManager, invoiceMarginDataVAT);
Collections.reverse(invoiceWordGroups);
insertAttributes(bottomAttributeWords, invoiceWordGroups, templateId, templateAttributeManager, invoiceMarginDataVAT);
My new code, I will test it aswell ofcourse, but even then some errors might remain to be in there, if my basic concepts are not oK. So would this have the same behaviour?
Collections.sort(invoiceWordGroups, new WordGroup.WordGroupComparator());
List<WordGroup> invoiceWordGroupsReverse = new ArrayList<>(invoiceWordGroups);
Collections.reverse(invoiceWordGroupsReverse);
insertAttributes(topAttributeWords, invoiceWordGroups, templateId, templateAttributeManager, invoiceMarginDataVAT);
insertAttributes(bottomAttributeWords, invoiceWordGroupsReverse, templateId, templateAttributeManager, invoiceMarginDataVAT);
The question is about invoiceWordGroups, which is of type List<WordGroup>. The reason I am changing it is because I will need to use the lists multiple times now, and constantly reversing it does definately not seem to be a good option.
If you check the Java source code the copy constructor for ArrayList creates a new list object which copies (hence the name copy constructor) the internal array (but even though it's a copy of the original array it still points to the same elements!!!). This internal array object is not shared therefore and it is the object which is responsible for what a given list object actually stores and in what order:
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
So reversing one list won't affect the order of the other. Same goes for adding/deleting elements from one list or the other.
You said that those are read-only objects then it will be ok but remember that even though both lists are different objects they still point to the same elements, so changing the state of object X in list1 will also be shown when accessing that object X with list2.
Related
I need to pass the same ArrayList from the main function to 3 different functions (all in different classes).
When the 1st function receives the ArrayList, it makes changes not only in the local copy of the list but also the original ArrayList in the main method gets modified. This indicates that the changes are being made at the memory address.
So, in the first function I copied the received arraylist into another arraylist of the same type, using:
for(int i=0;i<valuex.size();i++)
{
temp1=valuex.get(i);
VALUE.add(temp1);
}
This worked for the first time when I introduced this modification but then the same problem restored. Later I made three copies of the arraylist and passed each copy to each of the 3 functions. This too failed.
I didn't use
clone();
because this created the same problem some time back..
Please help...
You have described a shallow copy - you've made a fresh list, but you've got the same objects in each list.
If this isn't working for you, it suggests your method is modifying the list objects. To avoid this being a problem, you'll have to perform a deep copy - creating copies of each of your objects.
For example:
List<Foo> oldList = // ...
List<Foo> newList = new ArrayList<>();
for (Foo foo : oldList) {
Foo newFoo = new Foo(foo); // copy constructor
newList.add(newFoo);
}
When you passed a copy of the list to your three methods, you passed a reference to a copy of the list as a data structure. However, both lists, in memory, ended up pointing to the very same objects.
If your methods are changing the actual objects that the lists are pointing to, and you'd like to avoid that, then cloning the list is not enough: you must clone each and every object in the list as well.
By the way: the reason that clone() didn't work for you is that clone() only performs a shallow clone of the list: it creates a brand new ArrayList instance in memory, however the list's constituents are still the same objects (in memory) as the objects pointed-to by the original list.
You can just create a new ArrayList based on your existing arrayList before passing it in as argument.
List existingList = new ArrayList();
o1.m1(new ArrayList(existingList));
o2.m2(new ArrayList(existingList));
o3.m3(new ArrayList(existingList));
ArrayList contructor allows you to create a new ArrayList based on an existing collection.
http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#ArrayList(java.util.Collection)
I start learning the Java generic collection using Deitel Harvey book - but I am facing a difficulty understanding the three line of codes below - Do all of them perform the same operation on by intializing and adding the relevant values of array ( colors ) to the LinkList variable (list1). How does the second method and third method works - I am having a bit difficulty understanding how Arrays can viewed as a list.. As I know arrays are not dynamic data structure, they have fixed sized length, adding/ removing elements on array can not be done on running time comparing to Lists in general.
String[] colors = { "black", "white", "blue", "cyan" };
List< String > list1 = new LinkedList< String >();
// method 1 of initalizing and adding elments to the list
for (String color : colors)
list1.add(color);
// method 2 of initializing and adding elements to the list
List< String > list1 = new LinkedList< String > (Arrays.asList(colors));
// method 3 of initializing and adding elements to the list
List< String > list1 = Arrays.asList(colors);
Please help me understand my queries above, don't judge me as I am still new to this.
Thank you, Sinan
Actually knowledge of generics is not necessary for answering this question.
As you correctly identifier arrays are static in the sense that you can't add elements to them or remove them.
Lists, however, usually allow those operations.
The List returned by Arrays.asList() does have the add/remove methods (otherwise it would not be a valid List). However actually calling those methods will throw an UnsupportedOperationException exactly because you can't actually add elements to an array (for which this List is simply a view/wrapper).
Operations that don't structurally modify the list (i.e. that don't change the number of elements in the list) are entirely possible: set(int, E) works just fine on the List returned by Arrays.asList().
Arrays.asList returns a fixed-size list backed by the specified array.
It is actually a bridge between Array and Collection framework. But returned list write through to the array.
Only your first method does anything to the LinkedList you have initially assigned into list1. The other two assign a new, unrelated list to it. The third option assigns something that isn't a LinkedList, but a special implementation of the List interface backed by your String array. In the third case you won't be able to add/remove elements from the list, but you can iterate over it and update existing slots. Basically, it does what a plain array does, just through the List interface.
Arrays.asList creates a List from an Array. Arrays in general can't be viewed as lists in Java. They can only be wrapped in a list.
So method 2 is used to have a specific list implementation LinkedList in this case.
to Method 2, just check the Api here:
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/LinkedList.html#LinkedList(java.util.Collection)
For sure, Lists implement the Collections Interface so this Constructor will work here.
to Method 3, just check out the Api here: http://docs.oracle.com/javase/6/docs/api/java/util/Arrays.html#asList(T...)
Every time you are interested in implementation you can look into certain method. For example, by press Ctrl+left mouse button onto method or class.
// method 2 of initializing and adding elements to the list
List<String> list1 = new LinkedList<String> (Arrays.asList(colors));
This code leads to:
List<String> list1 = new LinkedList<String> (new ArrayList<String>(colors));
In constructor of ArrayList:
ArrayList(E[] array) {
if (array==null)
throw new NullPointerException();
a = array;
}
the actual array is copied to encapsulated private array field(link is copied).
Then in constructor of LinkedList:
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
Every element of passed collection is added to the LinkedList.
if you see the link below
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/LinkedList.html#LinkedList%28java.util.Collection%29
you will see the constructor of linked list class which is accepting a collection object as parameter.
Any in your post, the 2nd and 3 rd lines are passing an object of collection class(i.e Arrays.asList is finally giving a List which is a sub class of collection).
So both 2nd and 3rd lines fairly valid implementations.
More over you can observe one more good coding practice in all the 3 lines.
That is
writing code to interceptors than to classes
. (referring
LinkedList
instance with
List
interface)
Always try to refer your classes with interceptors which is a good practice
I want that b1 and b2 to have their own sets of elements, then b1 and b2 should have its own elements in memory so that when b1/b2 is modified, others should not be affected.
buffer is an ArrayList containing many elements
List<Integer> b1 = new ArrayList<Integer>(buffer.size()) ;
List<Integer> b2 = new ArrayList<Integer>(buffer.size()) ) ;
Collections.copy(b1, buffer);
Collections.copy(b2, buffer);
I am getting this exception:
Exception in thread "main"
java.lang.IndexOutOfBoundsException: Source does not fit in dest
at java.util.Collections.copy(Collections.java:531)
at Trees.containsSumPrint(Trees.java:243)
at Trees.main(Trees.java:125)
The ArrayList(int) constructor gives a List that has size 0 it only ensures that n elements can be added before it needs to reallocate the underlying array.
A better way you can copy the lists is:
b1.addAll(buffer);
b2.addAll(buffer);
The semantics are the same as when you would have first added buffer.size() nulls to each array and called Collections.copy(b1,buffer);
If you want a deep copy (the elements also copied) you are going to have to handle each element separately
for(MyObject obj:buffer){
b1.add(obj.clone());
b2.add(obj.clone());
}
The Collections.copy(...) javadoc says this:
"Copies all of the elements from one list into another. After the operation, the index of each copied element in the destination list will be identical to its index in the source list. The destination list must be at least as long as the source list. If it is longer, the remaining elements in the destination list are unaffected.".
The ArrayList(int) constructor creates an empty list whose capacity (not size!) is given by the argument.
Since b1 is initially empty, copying a non-empty list to it (using copy) will fail, since the precondition (in bold) does not hold true (in general).
Basically, Collections.copy(...) is the wrong method to use.
What you should really be doing is this:
List<Integer> b1 = new ArrayList<Integer>(buffer.size());
List<Integer> b2 = new ArrayList<Integer>(buffer.size());
b1.addAll(buffer);
b2.addAll(buffer);
I'm assuming that you don't really want to create new instances of the list elements. If you do, I should point out that creating new instances of Integer objects is waste of time since Integer (like the other wrapper classes and String) is an immutable class.
You want a deep copy of each element. There is no standard way to achieve this, because deep copying could involve copying nested references to (collections of) other objects. The best way to do this is create a copy constructor, java.lang.Integer happens to have one! So I think you should do something like:
List<Integer> buffer = Arrays.asList(new Integer[] { 0, 1, 2, 3, 4 });
List<Integer> b1 = new ArrayList<Integer>();
List<Integer> b2 = new ArrayList<Integer>();
for (Integer element : buffer) {
b1.add(new Integer(element));
b2.add(new Integer(element));
}
This actually creates TWO copies, one in each target list. If one of the lists may contain the original elements just do:
for (Integer element : buffer) {
b1.add(new Integer(element));
b2.add(element);
}
Note that there also exists the cloneable interface. I advise against using this because it is easy to make mistakes with referred classes, collections and subclassing. A copy constructor is much easier to get right. See this page for some corroboration.
EDIT: on re-reading, maybe you don't want deep copies, in which case you can use the 'addAll' method as described by others. This will allow you to create multiple collections of the same object instances. You can then modify the contents/order of objects in one collection without affecting other collections. However if you modify an object instance, this will obviously be reflected by all other collections as well.
Also, StephenC rightfully points out that my above example is nutty. I agree, one would never 'deep copy' Integers like that normally, but it would makes sense for custom objects containing collections/references which I thought was the issue here.
So I have an ArrayList in java. And what I'm doing is creating a new list with updated values. But I want to put those new values into the original ArrayList. This is important because I'm passing the original arraylist reference to an object that I no longer have access to, but I need to keep its contents up to date. Is there any way to make java copy the contents into a current reference? I hope that makes sense.
Also, I'd rather not clear the original arraylist and then loop through it pushing the new contents into it.
I need to do something like this:
ArrayList a;
ArrayList *b = a;
//pass b (the pointer) to something
ArrayList c;
*b = c;
I really hope I got that (pseudo) c++ code correct or I'll look pretty stupid =P
I can see how I'm not being clear, it's kind of complicated (this is in android so it's across a couple activities) so let me see if I can get my point across better.
Class1{
ArrayList a;
method1(){
a = Class2.getA();
method_that_uses_a(a);
}
}
Class2{
ArrayList getA(){
ArrayList a = new ArrayList
a = getDataFromWebsite();
return a;
}
Class1's method1() gets called periodically by another portion of code. But I need the reference to a stay the same, but the contents to change to the new contents.
I think your question is unclear, what do you mean by
"And what I'm doing is creating a new list with updated values. But I want to put those new values into the original ArrayList. This is important because I'm passing the original arraylist reference to an object that I no longer have access to, but I need to keep its contents up to date. Is there any way to make java copy the contents into a current reference? I hope that makes sense."
When you do
List a = new ArrayList
List b = a
you have one ArrayList object, and two references to the object, a and b.
Also note that there is an addAll method that you can use to add members of one collection to another collection. Note that I believe addAll does not do a deep copy, so if you use it both lists have copies of the same reference. So if list a has references a1, a2, a3, and you do b.addAll(a), b now has references to the objects that a1...a3 point to...
The array classes all expose public clone() methods, however, so if a
shallow copy of the array is sufficient then
return (ElementType[]) myArray.clone();
Makes for a nice, simple, easy to read paradigm. If "ElementType" is a
primitive type then there is no distinction between shallow and deep
copying, and if it is immutable then performing a deep copy would be
wasteful. In some other circumstances a shallow copy is what you would
want anyway (you want the receiver to be able to mutate the returned
array elements with effects visible to the host object).
If you find that you really do want to make a deep copy of the array
then you might want to consider whether your design can be improved; it
is not necessarily so, but I wouldn't be surprised.
In Java as I hope you;ve found out by now, all variables are references. Among other things this means that unless they are assigned to an object they don't 'point' at anything. You need to write:
ArrayList a = new ArrayList();
or a doesn't 'point to' an actual object - it's just null.
If you write:
ArrayList a = new ArrayList();
ArrayList b = a;
modify(b);
ArrayList c = b;
then there is only one ArrayList. All the variables a, b and c refer to it. Any modifications done in the 'modify' method apply to it, and will be reflect in the state of a, b and c.
You're C++ code says this:
ArrayList a; //make arraylist
ArrayList *b = a; //set b to point to same arraylist as a
//pass b somewhere
ArrayList c; //make arraylist
*b = c; //variable b in THIS PROGRAM now points to c. arraylist is unchanged.
You want to update the arraylist, not the pointer to it, as that pointer only 'exists' in the current context. The code you passed the arraylist to doesn't give a darn if a pointer back in who-knows-where now points to the same arraylist that its using. It's the actual object the other code cares about. To update that object, you can just call methods on it, like a.add(bar)
BUT there's more to it. If you call a function you don't control (now known as foo) and pass it an arraylist, that's all fine. But if you want to update it from the code calling foo, you run into issues. Imagine if an object you were working with could change randomly at any time. All sorts of bad stuff could happen. If you really need this capability (and we can help you judge if you do), look into the topic of threading.
I have a question. What is wrong with regards to the below code:
ArrayList tempList2 = new ArrayList();
tempList2 = getXYZ(tempList1, tempList2);
//method getXYZ
getXYZ(ArrayList tempList1, ArrayList tempList2) {
//does some logic and adds objects into tempList2
return tempList2;
}
The code will get executed but it seems by passing tempList2 to the getXYZ method argument, it is doing object recycling.
My question is, Is recycling the tempList2 arraylist object correct?
My question is, Is recycling the tempList2 arraylist object correct?
I don't quite know what you mean by "recycling". This doesn't appear to be a case where the application is recycling objects in an attempt to avoid allocating new objects. (That is the normal meaning of "recycling" in Java.)
If getXYZ is called multiple times with the same tempList2 object, then this is simply a way of aggregating stuff into a single list. The fact that getXYZ returns an ArrayList leaves open the possibility that the method implementation may be changed to return a different ArrayList instance. That's not a problem per se, but it might be if the caller doesn't assign the result of the call appropriately.
If getXYZ is only called once for any given tempList2 object, then this looks a bit strange.
In summary, this code looks a bit fishy, and is fragile if someone changes the implementation of getXYZ. However, it is not down-right wrong, and there may be some good reason (or historical reason) for doing things this way that is not apparent in the small chunks of code you included in the question.
EDIT - in response to this comment (inlined to make it readable)
Actually, the reason for the above code is, I wanted to avoid creating two arraylist objects. For ex: the conventional method would be
ArrayList tempList2 = new ArrayList();
tempList2 = getXYZ(tempList1);
//method getXYZ
getXYZ(ArrayList tempList1) {
ArrayList tempList = new ArrayList();
//INSTANTIATED ONLY ONCE
//does some logic and adds objects into tempList
return tempList;
}
The real conventional way of doing this would be:
ArrayList tempList2 = getXYZ(tempList1);
or
ArrayList tempList2;
// some intermediate lines
tempList2 = getXYZ(tempList1);
Neither of these require creating the unnecessary ArrayList instance of your approach, and neither require passing 2 ArrayList instances into the getXYZ method.
There's really no such concept as "object recycling" here. Nothing's being garbage collected, if that's what you're interested in... at least, not if there are no lines like this:
tempList2 = new ArrayList();
within getXYZ().
The reference to the ArrayList is passed into the method and then returned. That's all. It's just a reference - it's not the object itself.
Now you could say it's slightly odd for the method to return the ArrayList reference which was passed into it... usually that's used for fluent interfaces, but it doesn't seem to be the case here.
You're passing the value of the tempList2 reference. The object itself isn't copied. So when the passed-in tempList2 is modified, the caller sees the changes. This is also called call-by-sharing
So there's nothing wrong, once you understand what's going on.