Collections.copy problem - java

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.

Related

aliasing with StringBuffer

I am running this code:
public class testttt {
public static void main(String[] args){
ArrayList<StringBuffer> listOne = new ArrayList <StringBuffer>();
listOne.add(new StringBuffer("One"));
listOne.add(new StringBuffer("Two"));
listOne.add(new StringBuffer("Three"));
ArrayList <StringBuffer> listTwo = new ArrayList <StringBuffer>(listOne);
listOne.add(new StringBuffer("Four"));
for (StringBuffer str : listTwo) {
str.append("2");
}
System.out.println("List One: " + listOne);
System.out.println("List Two: " + listTwo);
}
}
I thought by having the "new ArrayList" declaration when initializing listTwo I would have created a distinct array that would be independent from listOne. However, the output is:
List One: [One2, Two2, Three2, Four]
List Two: [One2, Two2, Three2]
I have a suspicion that the listTwo initialization only copied over the references from listOne, but I thought it would have been handled by the "new ArrayList" section.
Any explanation would be greatly appreciated!
By using the listOne for the listTwo construction, you are saying: "please create a new list, and then copy all elements from the first list into that second list".
And then of course, java is doing "call-by-value". This means: "copying" doesn't mean the creation of new StringBuffers. It means that both lists hold references to the same StringBuffer objects.
Thus when you iterate the second list, and modify members of the second list, you see the effects on the first list as well.
So, the "real" answer is: always understand the concepts you are using; the "real" message here isn't the explanation; but the fact that one core part of being a programmer is to be very precise about the code you write, and to really understand each and every tiny bit of statement your put in your code. Everything has a certain meaning; and if you don't know them, your code will keep "surprising" you.
You first create a List (listOne) that contains instances of StringBuffer.
Then you create another List (listTwo) that contains the same instances of StringBuffer. However, this is a different list.
Then you add to listOne one additional element. You change just listOne, not listTwo.
Then you change each StringBuffer instance within listTwo. Since listOne and listTwo contain, among others, the same instances of StringBuffer, this change is visible in listOne as well.
Note: you didn't put copies of the StringBuffer instances into listTwo, but references to the same objects!
Constructor and other methods perform shallow copy. As mentioned in this answer,
Shallow copies duplicate as little as possible. It tries to create a reference to same old object hence it creates a copy the collection structure, not the elements. With a shallow copy, two collections share the individual elements.
whereas a Deep copies everything and create duplicates. It copies all of the elements in the original collection duplicated.
You can use Collections utility class if you want to create a deep copy.
ArrayList <StringBuffer> listTwo = new ArrayList <StringBuffer>(listOne.size());
Collections.copy(listTwo , listOne);
When you use the copy constructor for ArrayList, you're telling the new list to copy the elements of the old list. What collections actually store in Java is references, and you get what's called a "shallow copy": copies of references, but the same shared objects.
When you then modify those objects, the changes are visible regardless of which list of references you use to get to them.

Java construct new ArrayList without deep clone

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.

Java generic collections

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

Not specifying which list implementation

I have a doubt considering changing this :
List<String> elements = new ArrayList<String>();
elements = elementDao.findElementsById(elementId);
to
List<String> elements;
elements = elementDao.findElementsById(elementId);
(I'm using DAO with Hibernate)
Can this cause any errors or exceptions (the fact that i'm not specifying which List implementation should be returned) ?
The first one creates a new arraylist for nothing. The created list is just garbage that has to be collected.
The second one is better, but should be reduced to
List<String> elements = elementDao.findElementsById(elementId);
You seem to be thinking that the assignment operator could be used to fill a list created by the caller. This is not the case. the assignment operator just takes the reference to the list created by the DAO (and which could be any kind of List), and assigns this reference to the variable.
You can safely change it because:
List<String> elements = new ArrayList<String>();
creates a new ArrayList and assigns it to elements, then
elements = elementDao.findElementsById(elementId);
throws the original ArrayList away (and marks it to be garbage collected) and assign elements to it the List created inside elementDao, so the second approach is just as safe and more efficient.

How can I slice an ArrayList out of an ArrayList in Java?

How do I get an array slice of an ArrayList in Java? Specifically I want to do something like this:
ArrayList<Integer> inputA = input.subList(0, input.size()/2);
// where 'input' is a prepouplated ArrayList<Integer>
So I expected this to work, but Java returns a List - so it's incompatible. And when I try to cast it, Java won't let me. I need an ArrayList - what can I do?
In Java, it is good practice to use interface types rather than concrete classes in APIs.
Your problem is that you1 are using ArrayList (probably in lots of places) where you should really be using List. As a result you created problems for yourself with an unnecessary constraint that the list is an ArrayList.
This is what your code should look like:
List input = new ArrayList(...);
public void doSomething(List input) {
List inputA = input.subList(0, input.size()/2);
...
}
this.doSomething(input);
1 - Based on your comments, "you" was actually someone else ... who set this problem in an interview question. It is possible that this was actually a trick question, designed to see how you would cope with creating a (real) slice of an ArrayList that was a assignment compatible with ArrayList.
Your proposed "solution" to the problem was/is this:
new ArrayList(input.subList(0, input.size()/2))
That works by making a copy of the sublist. It is not a slice in the normal sense. Furthermore, if the sublist is big, then making the copy will be expensive.
If you are constrained by APIs that you cannot change, such that you have to declare inputA as an ArrayList, you might be able to implement a custom subclass of ArrayList in which the subList method returns a subclass of ArrayList. However:
It would be a lot of work to design, implement and test.
You have now added significant new class to your code base, possibly with dependencies on undocumented aspects (and therefore "subject to change") aspects of the ArrayList class.
You would need to change relevant places in your codebase where you are creating ArrayList instances to create instances of your subclass instead.
The "copy the array" solution is more practical ... bearing in mind that these are not true slices.
I have found a way if you know startIndex and endIndex of the elements one need to remove from ArrayList
Let al be the original ArrayList and startIndex,endIndex be start and end index to be removed from the array respectively:
al.subList(startIndex, endIndex + 1).clear();
If there is no existing method then I guess you can iterate from 0 to input.size()/2, taking each consecutive element and appending it to a new ArrayList.
EDIT: Actually, I think you can take that List and use it to instantiate a new ArrayList using one of the ArrayList constructors.
Although this post is very old. In case if somebody is looking for this..
Guava facilitates partitioning the List into sublists of a specified size
List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
List<List<Integer>> subSets = Lists.partition(intList, 3);
This is how I solved it. I forgot that sublist was a direct reference to the elements in the original list, so it makes sense why it wouldn't work.
ArrayList<Integer> inputA = new ArrayList<Integer>(input.subList(0, input.size()/2));

Categories

Resources