Hi Here i am trying to sublist the items from list and print them 5 for each iteration.
here in the following code it is printing same items every time
for(int i=0;i<4;i++)
{
List<Card> l=a.subList(a.size()-5, a.size());
System.out.println(l);
}
But here it is printing different items as if it is removing 5 from the list each time
for(int i=0;i<4;i++){
int deckSize = a.size();
List<Card> handView = a.subList(deckSize-5, deckSize);
ArrayList<Card> hand = new ArrayList<Card>(handView);
handView.clear();
System.out.println(hand);
}
what is the difference between the above two code snippets
You should have a read at the API for List.
The returned list is backed by this
list, so non-structural changes in the
returned list are reflected in this
list, and vice-versa.
So in each case the list you are creating is not a new copy of the elements from the original list, but just a view into the original list. In the second example you are calling clear on the new list, which is actually clearing those elements in the original list, hence the behaviour you are seeing.
it seems that using .clear() on the result of .subList() removes the returned items form the original list
This is the designed behaviour of subList(). See the Javadoc for List.subList().
There's even an example doing almost exactly the same thing. (Removing items from a view)
Check out the doc for subList. It returns a list backed up by original list and is useful as a view. If you modify the sublist it will modify the main list. Since you cleared elements from it the original list is also modified.
List is interface.ArrayList is the class of implemention of List interface.
Related
I am having an issue in understanding this.
while we do
List<Integer> list = Arrays.asList(array);
we can not use methods like add, remove on that list. I know that Arrays.asList() returns a fixed-sized list.
What I don't understand is if we create a list with initial capacity specified like
List<Integer> list2 = new ArrayList<Integer>(10);
we can perform all the operations on that list. What is the difference between fixed-sized list and list with initial capacity specified?
I have read many answers on this but having such a hard time understanding this. Can anyone explain?
Thanks.
Arrays.asList(array) returns an object of type java.util.Arrays.ArrayList, which does not support add and remove operations.
While the code below will return an object of type java.util.ArrayList, which supports add and remove operations.
List<Integer> list2 = new ArrayList<Integer>(10);`
Very simply, Arrays.asList is so you can use List methods with an array. ArrayList(int) is for when you need to create a really large ArrayList and want to help speed things up a bit.
In more detail: the List returned by asList is intended as a wrapper to an array. Since you cannot resize an array, the methods that change the size of a List are unimplemented. Most of the time I just use asList to add a fixed number of elements to a collection simply. eg.
new ArrayList<String>(Arrays.asList("hello", "world"));
Confusingly, the implementation of ArrayList is very similar -- it's a List backed by an array. However, ArrayList allows you to change it's size. To do this it keeps a separate fields about the how many objects are in the list and the length of the backing array. Add an element and the ArrayList just sets array[size] to the element and then increments the size field. But what if array[size] is out of bounds? At this point the ArrayList creates a new, larger array and copies over the elements from the previous backing array. However, if you are creating a large List then this constant creation of new backing arrays can start to take up a lot of time. As such, if you know the approximate number of elements that will be in the List you can use this to inform the ArrayList about the size of the initial backing array it should create. This is what the ArrayList(int) constructor is for. Only in exceptional circumstances will you need to worry about giving the ArrayList a length hint.
So, sort of in continuation of this post: What is the difference between ArrayList.clear() and ArrayList.removeAll()?... Are there certain situations where it is actually better to use removeAll() instead of clear()?
Also, to add onto this question, if I know I'm clearing all the contents of an ArrayList, would it be ok to set it to a new ArrayList?
ArrayList myList = new ArrayList<String>();
myList.add("a");
myList.add("b");
// instead of using: myList.clear();
myList = new ArrayList<String>()
If the above is ok to do, again, why use clear() vs setting to a new ArrayList? Creating a new, empty ArrayList is faster than O(n).
Why use clear() instead of creating a new ArrayList? Several reasons:
You might not be allowed to reassign a reference field that points to an ArrayList, so you can clear an existing list but not put a new one in its place. For example:
class MyData {
// Can clear() but not reassign
final List<Object> list = new ArrayList<>();
}
The variable might be declared as List. The actual type could be LinkedList and you want to preserve it instead of replacing the implementation with an ArrayList.
It's not necessarily true that clear() is O(n) time. One implementation strategy is to nullify all the existing elements in the backing array in O(n) time. But another equally valid implementation is to throw away that internal array and replace it with a new one, preferably a short array for O(1) time.
There is no ArrayList.removeAll().
But there is an ArrayList.removeAll(Collection).
The method clear() should be faster than removeAll(Collection) because removeAll(Collection) does some comparisions to decide if an object should be removed from the list. The clear() method simply removes everything without thinking.
if you want to remove a specific collection from your list but you don't want to remove everything, you will use remove all with the collection that you want to remove.
Can anyone explain why subList() doesn't behave as subSet() method, and throws a ConcurrentModificationException, while subSet not. Both methods created a Backed Collection, so probably the subList() method designers created this method relying on a unmodifiable original list, but wouldn't be better if all Backed Collections had the same behavior (like subSet())?
// code
public class ConcurrentModificationException {
public static void main(String[] args) {
String[] array = {"Java","Python","Pearl","Ada","Javascript","Go","Clojure"};
subListEx(array);
subSetEx(array);
}
private static void subListEx(String[] array) {
List<String> l = new ArrayList<String>(Arrays.asList(array));
List<String> l2 = l.subList(2, 4);
System.out.println(l.getClass().getName());
// l.add("Ruby"); // ConcurrentModificationException
// l.remove(2); // ConcurrentModificationException
l2.remove("Ada"); // OK
for (String s:l) { System.out.print(s+", "); }
System.out.println();
for (String s:l2) { System.out.print(s+", "); }
}
private static void subSetEx(String[] array) {
SortedSet<String> s1 = new TreeSet<String>(Arrays.asList(array));
SortedSet<String> s2 = s1.subSet("Java", "Python");
s1.remove("Ada");
for (String s:s1) { System.out.print(s+", "); }
System.out.println();
for (String s:s2) { System.out.print(s+", "); }
}}
Thanks in advance!
It's already clear that the behaviour is as per documented. But I think your main question is why is the behaviour different for ArrayList and TreeSet. Well, it has to do with how data is stored internally in both the collections.
An ArrayList internally uses an array to store the data, which is re-sized as the size of ArrayList dynamically increases. Now, when you create a subList of your given list, original list with the specified indices is associated with the subList. So, any structural changes (that screws the indexing of the original array), done in the original list, will make the index stored as a part of sublist meaningless. That is why any structural changes is not allowed in case of ArrayList#subList method.
The subList method returns you an instance of an inner class named SubList inside the ArrayList class, which looks like:
private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
As you see, the SubList contains a reference to the original list. And the parentOffset is nothing but the starting index of the subList you are creating. Now modifying the original list will possibly change the value at fromIndex in original list, but not inside the SubList. In that case, parentOffset in SubList class and fromIndex in original list, will point to different array elements. It might also be possible that at some point the original array becomes shorter enough to invalidate index stored in the SubList and make it OutOfRange. This is certainly not desirable, and the semantics of the subList returned is considered undefined, on such structural changes to original list.
On the other hand, a TreeSet stores it's data internally in a TreeMap. Now as there is no such concept of indices in a Map, there is no issue of indices breaking up. A Map is nothing but a mapping of key-value pair. Creating a SubSet involves creating a SubMap which is backed by the original Map. Modifying the original Set will just require the corresponding key-value mapping being invalidated, thus propagating the changes to the subMap created for the subSet.
The contract for List.subList(int, int) covers this. Here are the relevant parts, emphasis mine.
The returned list is backed by this list, so non-structural changes in
the returned list are reflected in this list, and vice-versa.
...
The semantics of the list returned by this method become undefined if the
backing list (i.e., this list) is structurally modified in any way
other than via the returned list. (Structural modifications are those
that change the size of this list, or otherwise perturb it in such a
fashion that iterations in progress may yield incorrect results.)
In your sample, you are making structural changes to the backing list, thus the results are undefined.
The Javadoc for both are very clear on this:
From ArrayList.sublist()
The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list. (Structural modifications are those that change the size of this list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.)
So while you're not guaranteed to receive a ConcurrentModificationException, it's not out of the question. The behavior is undefined if you modify the backing list.
Whereas...
From TreeSet.subSet()
Returns a view of the portion of this set whose elements range from fromElement, inclusive, to toElement, exclusive. (If fromElement and toElement are equal, the returned set is empty.) The returned set is backed by this set, so changes in the returned set are reflected in this set, and vice-versa. The returned set supports all optional set operations that this set supports.
There is no cautioning here and modifications to the backing set are fine and pose no issue.
The documentation is very clear: aList.subList() returns a view of the list. The returned object is backed by the original list, or so you should assume.
The documentation is also very clear regarding Sets:
The iterators returned by this class's iterator method are fail-fast: if the set is modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException.
Why producing the issue with Lists is easier than with Set's? Because when in a Set you delete an element the effect is very clear: the element is not in the set anymore, both for the set and any subset. Same thing with insert operations. But when you delete an element from a List, does it mean that sublists adjust their indices accordingly (and possibly size), or they just preserve their size by adding elements from the right or the left? And, in the second case, what if the backing list becomes too short? The behavior is too complex to define. And also too limiting for implementations.
Example: A list is [a,b,c,d], a sublist is list.subList(1,3) ([b,c]). a is removed from list. The effect in list is clear. But does sublist stays [b,c] (changing its range to (0,2)), or becomes [c,d] (preserving its range in (1,3))?
Example: A list is [a,b,c,d], a sublist is list.subList(1,3) ([b,c]). b is removed from list. Again, the effect in list is clear. But does sublist become [c] (1,2), [a,c] (0,2), or [c,d] (1,3)?
is it somehow possible to get a sorted List view of a List with the elements from the original List, modify the elements, for example set a property, but the original List should reflect the changes?
Background is I'm sorting two Lists, then for each equal element I'm setting a property (basically it's the intersection) but finally I need the unsorted List with the modified elements.
kind regards,
Johannes
Probably the simplest thing to do is add the elements to a new list, sort that list, and when you modify the elements, the original elements will still be modified...
List<?> origA;
List<?> origB;
List<?> newA = new ArrayList<?>(origA);
List<?> newB = new ArrayList<?>(origB);
Collections.sort(newA);
Collections.sort(newB);
// do mods
If the List holds references to objects (not primitive data types), then just copy the list, sort it and modify the elements.
Does it have to be a list? If you keep your elements in a TreeSet, they will always be sorted as you iterate through them, even after you add/remove the elements. Remember though that modifying an element already in the TreeSet may break the sort order. You can remove and add the element to the TreeSet to get around that.
If you have to use a list, you can use Collections.sort(List list) after adding or modifying an element. Of course, if you have to call it often, there will be a performance hit. If performance is a concern, you can just insert the new element (or move the modified one) to maintain the sorted order, which will be cheaper than sorting it: O(n) vs O(n*log(n))
I have a list that contain elements, I want to remove the duplicates. I have tried to do that but it didn't work.
This is the code :
//list is the original list containing duplicates
Iterator it1=list.iterator();//iterator1
while (it1.hasNext())
{
Iterator it2=list.iterator(); //iterator2
if(it1.next().equals(it2.next()))
{
//liststeps is the list without duplicates
liststeps.add(it1.next());
}
}
System.out.println("multiple list "+list.toString());
System.out.println("list "+liststeps.toString());
Results:
multiple list [Open, In Progress, Waiting Customer Test, Closed, Open, Step11, Step22, Open, In Progress, Waiting Customer Test, Closed]
list [In Progress, Step11, In Progress]
Use a Set instead of a List. You can use addAll() to add your list to the Set, then convert it back to a List or just use the Set. And if the order is relevant, use a LinkedHashSet.
And just a suggestion: use for-each instead of iterator... it´s easier :-)
Simply construct a HashSet or a TreeSet from your list:
Set<String> s = new HashSet<String>(list);
This will remove the duplicates, but will likely re-order the elements. If you want to preserve the order in which the (unique) elements appear in the original list, use LinkedHashSet:
Set<String> s = new LinkedHashSet<String>(list);
Lastly, if you need the output to be a list, convert the set to a list like so:
List<String> out = new ArrayList<String>(s);
You are calling next way to often, it always moves to the next element.
if(it1.next().equals(it2.next())) {
liststeps.add(it1.next());
}
This code actually adds the next element after the one you compared to your list.
Use a Set/for-each as Tobiask pointed out.