Not specifying which list implementation - java

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.

Related

Collections.singleton() methods does not work as documentation?

I have tested Collections.singleton() method, how to work, but i see that it is not work as what documentation say?
List arraylist= new ArrayList();
arraylist.add("Nguyen");
arraylist.add("Van");
arraylist.add("Jone");
List list = Collections.singletonList(arraylist);// contains three elements
System.out.println(list.size());// right
As what documentation say, The method call returns an immutable list containing only the specified object,A singleton list contains only one element and a singleton HashMap includes only one key. A singleton object is immutable (cannot be modified to add one more element),but when what thing i see in my code that list contains three elements("Nguyen","Van","Jone").
Anybody can explain for me why?? Thanks so much !!
The returned List is a List of Lists. In this case, the returned list of lists itself is immutable, not the contained List. Also the returned list contains only one element, not three: the arraylist variable itself is considered an element and is the only element stored in the list returned by Collections.singletonList. In other words, the statement Collections.singletonList(arraylist) does not create a list that contains all elements of the provided list.
It would have been much more obvious if you use generics:
List<String> arraylist= new ArrayList<>();
arraylist.add("Nguyen");
arraylist.add("Van");
arraylist.add("Jone");
List<List<String>> list = Collections.singletonList(arraylist);
What the documentation says is that if you do the following:
List list = Collections.singletonList(arraylist);
list.add(new ArrayList());
then this would throw an exception at runtime.

Adding Arrays in to ArrayList

I can addAll array elements in to ArrayList by following two ways,
First,
List<String> list1 = new ArrayList<String>();
list1.addAll(Arrays.asList("23,45,56,78".split(",")));
System.out.println(list1);
Second,
List<String> list2 = new ArrayList<String>();
list2.addAll(new ArrayList<String>(Arrays.asList("23,45,56,78".split(","))));
System.out.println(list2);
Both works fine. And my question is Is there any difference between these two. And which one can be used for better practice Why ?
Both approaches produce the same result, so in that respect they are equivalent.
The second one, however, is wasteful. Arrays.asList does not allocate additional memory - it just wraps a given array in a List-like API. Creating a new ArrayList, on the other hand, allocates, albeit temporarily, another array with the same size, and copies all the values from the source array to the internal array of the ArrayList's implementation.
With small arrays it's doubtful you'd even notice the difference, but the first approach is definitely more efficient.
The addAll method is defined on the Collection interface. With both examples, you are passing in a List. You aren't keeping the ArrayList you're creating in the second example, but it's not even necessary. Arrays.asList sends the List just fine into addAll by itself. The creation of the unnecessary ArrayList in the second example is unnecessary, so the first example is preferred.
Of the two you listed, the first is better. The second example creates another ArrayList object that isn't needed. Both would be functionally the same, but the first is more efficient.
As to the best practice, you can do this kind of thing in 1 line, not 2.
List<String> list2 = new ArrayList<String>(Arrays.asList("23,45,56,78".split(",")));
You can create a list by passing the arguments into it's constructor, a little cleaner than calling .addAll after creating the Object

How to copy a java.util.List into another java.util.List

I have a List<SomeBean> that is populated from a Web Service. I want to copy/clone the contents of that list into an empty list of the same type. A Google search for copying a list suggested me to use Collections.copy() method. In all the examples I saw, the destination list was supposed to contain the exact number of items for the copying to take place.
As the list I am using is populated through a web service and it contains hundreds of objects, I cannot use the above technique. Or I am using it wrong??!! Anyways, to make it work, I tried to do something like this, but I still got an IndexOutOfBoundsException.
List<SomeBean> wsList = app.allInOne(template);
List<SomeBean> wsListCopy=new ArrayList<SomeBean>(wsList.size());
Collections.copy(wsListCopy,wsList);
System.out.println(wsListCopy.size());
I tried to use the wsListCopy=wsList.subList(0, wsList.size()) but I got a ConcurrentAccessException later in the code. Hit and trial. :)
Anyways, my question is simple, how can I copy the entire content of my list into another List? Not through iteration, of course.
Just use this:
List<SomeBean> newList = new ArrayList<SomeBean>(otherList);
Note: still not thread safe, if you modify otherList from another thread, then you may want to make that otherList (and even newList) a CopyOnWriteArrayList, for instance -- or use a lock primitive, such as ReentrantReadWriteLock to serialize read/write access to whatever lists are concurrently accessed.
This is a really nice Java 8 way to do it:
List<String> list2 = list1.stream().collect(Collectors.toList());
Of course the advantage here is that you can filter and skip to only copy of part of the list.
e.g.
//don't copy the first element
List<String> list2 = list1.stream().skip(1).collect(Collectors.toList());
originalArrayList.addAll(copyArrayofList);
Please keep on mind whenever using the addAll() method for copy, the contents of both the array lists (originalArrayList and copyArrayofList) references to the same objects will be added to the list so if you modify any one of them then copyArrayofList also will also reflect the same change.
If you don't want side effect then you need to copy each of element from the originalArrayList to the copyArrayofList, like using a for or while loop. for deep copy you can use below code snippet.
but one more thing you need to do, implement the Cloneable interface and override the clone() method for SomeBean class.
public static List<SomeBean> cloneList(List<SomeBean> originalArrayList) {
List<SomeBean> copyArrayofList = new ArrayList<SomeBean>(list.size());
for (SomeBean item : list) copyArrayofList.add(item.clone());
return copyArrayofList;
}
I tried to do something like this, but I still got an IndexOutOfBoundsException.
I got a ConcurrentAccessException
This means you are modifying the list while you are trying to copy it, most likely in another thread. To fix this you have to either
use a collection which is designed for concurrent access.
lock the collection appropriately so you can iterate over it (or allow you to call a method which does this for you)
find a away to avoid needing to copy the original list.
Starting from Java 10:
List<E> oldList = List.of();
List<E> newList = List.copyOf(oldList);
List.copyOf() returns an unmodifiable List containing the elements of the given Collection.
The given Collection must not be null, and it must not contain any null elements.
Also, if you want to create a deep copy of a List, you can find many good answers here.
There is another method with Java 8 in a null-safe way.
List<SomeBean> wsListCopy = Optional.ofNullable(wsList)
.map(Collection::stream)
.orElseGet(Stream::empty)
.collect(Collectors.toList());
If you want to skip one element.
List<SomeBean> wsListCopy = Optional.ofNullable(wsList)
.map(Collection::stream)
.orElseGet(Stream::empty)
.skip(1)
.collect(Collectors.toList());
With Java 9+, the stream method of Optional can be used
Optional.ofNullable(wsList)
.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList())
I tried something similar and was able to reproduce the problem (IndexOutOfBoundsException). Below are my findings:
1) The implementation of the Collections.copy(destList, sourceList) first checks the size of the destination list by calling the size() method. Since the call to the size() method will always return the number of elements in the list (0 in this case), the constructor ArrayList(capacity) ensures only the initial capacity of the backing array and this does not have any relation to the size of the list. Hence we always get IndexOutOfBoundsException.
2) A relatively simple way is to use the constructor that takes a collection as its argument:
List<SomeBean> wsListCopy=new ArrayList<SomeBean>(wsList);
I was having the same problem ConcurrentAccessException and mysolution was to:
List<SomeBean> tempList = new ArrayList<>();
for (CartItem item : prodList) {
tempList.add(item);
}
prodList.clear();
prodList = new ArrayList<>(tempList);
So it works only one operation at the time and avoids the Exeption...
You can use addAll().
eg : wsListCopy.addAll(wsList);
re: indexOutOfBoundsException, your sublist args are the problem; you need to end the sublist at size-1. Being zero-based, the last element of a list is always size-1, there is no element in the size position, hence the error.
I can't see any correct answer. If you want a deep copy you have to iterate and copy object manually (you could use a copy constructor).
You should use the addAll method. It appends all of the elements in the specified collection to the end of the copy list. It will be a copy of your list.
List<String> myList = new ArrayList<>();
myList.add("a");
myList.add("b");
List<String> copyList = new ArrayList<>();
copyList.addAll(myList);
just in case you use Lombok:
mark SomeBean with the following annotation:
#Builder(toBuilder = true, builderMethodName = "")
and Lombok will perform a shallow copy of objects for you using copy constructor:
inputList.stream()
.map(x -> x.toBuilder().build())
.collect(Collectors.toList());
subList function is a trick, the returned object is still in the original list.
so if you do any operation in subList, it will cause the concurrent exception in your code, no matter it is single thread or multi thread.

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

Collections.copy problem

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.

Categories

Resources