Here's a nice pitfall I just encountered.
Consider a list of integers:
List<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(6);
list.add(7);
list.add(1);
Any educated guess on what happens when you execute list.remove(1)? What about list.remove(new Integer(1))? This can cause some nasty bugs.
What is the proper way to differentiate between remove(int index), which removes an element from given index and remove(Object o), which removes an element by reference, when dealing with lists of integers?
The main point to consider here is the one #Nikita mentioned - exact parameter matching takes precedence over auto-boxing.
Java always calls the method that best suits your argument. Auto boxing and implicit upcasting is only performed if there's no method which can be called without casting / auto boxing.
The List interface specifies two remove methods (please note the naming of the arguments):
remove(Object o)
remove(int index)
That means that list.remove(1) removes the object at position 1 and remove(new Integer(1)) removes the first occurrence of the specified element from this list.
You can use casting
list.remove((int) n);
and
list.remove((Integer) n);
It doesn't matter if n is an int or Integer, the method will always call the one you expect.
Using (Integer) n or Integer.valueOf(n) is more efficient than new Integer(n) as the first two can use the Integer cache, whereas the later will always create an object.
I don't know about 'proper' way, but the way you suggested works just fine:
list.remove(int_parameter);
removes element at given position and
list.remove(Integer_parameter);
removes given object from the list.
It's because VM at first attempts to find method declared with exactly the same parameter type and only then tries autoboxing.
list.remove(4) is an exact match of list.remove(int index), so it will be called. If you want to call list.remove(Object) do the following: list.remove((Integer)4).
Any educated guess on what happens when you execute list.remove(1)? What about list.remove(new Integer(1))?
There is no need to guess. The first case will result in List.remove(int) being called, and the element at position 1 will be removed. The second case will result in List.remove(Integer) being called, and the element whose value is equal to Integer(1) will be removed. In both cases, the Java compiler selects the closest matching overload.
Yes, there is potential for confusion (and bugs) here, but it is a fairly uncommon use-case.
When the two List.remove methods were defined in Java 1.2, the overloads were not ambiguous. The problem only arose with the introduction of generics and autoboxing in Java 1.5. In hind-sight, it would have been better if one of the remove methods had been given a different name. But it is too late now.
Note that even if the VM did not do the right thing, which it does, you could still ensure proper behaviour by using the fact that remove(java.lang.Object) operates on arbitrary objects:
myList.remove(new Object() {
#Override
public boolean equals(Object other) {
int k = ((Integer) other).intValue();
return k == 1;
}
}
Simply I did like following as suggested by #decitrig in accepted answer first comment.
list.remove(Integer.valueOf(intereger_parameter));
This helped me. Thanks again #decitrig for your comment. It may help for some one.
Well here is the trick.
Let's take two examples here:
public class ArrayListExample {
public static void main(String[] args) {
Collection<Integer> collection = new ArrayList<>();
List<Integer> arrayList = new ArrayList<>();
collection.add(1);
collection.add(2);
collection.add(3);
collection.add(null);
collection.add(4);
collection.add(null);
System.out.println("Collection" + collection);
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(null);
arrayList.add(4);
arrayList.add(null);
System.out.println("ArrayList" + arrayList);
collection.remove(3);
arrayList.remove(3);
System.out.println("");
System.out.println("After Removal of '3' :");
System.out.println("Collection" + collection);
System.out.println("ArrayList" + arrayList);
collection.remove(null);
arrayList.remove(null);
System.out.println("");
System.out.println("After Removal of 'null': ");
System.out.println("Collection" + collection);
System.out.println("ArrayList" + arrayList);
}
}
Now let's have a look at the output:
Collection[1, 2, 3, null, 4, null]
ArrayList[1, 2, 3, null, 4, null]
After Removal of '3' :
Collection[1, 2, null, 4, null]
ArrayList[1, 2, 3, 4, null]
After Removal of 'null':
Collection[1, 2, 4, null]
ArrayList[1, 2, 3, 4]
Now let's analyze the output:
When 3 is removed from the collection it calls the remove() method of the collection which takes Object o as parameter. Hence it removes the object 3.
But in arrayList object it is overridden by index 3 and hence the 4th element is removed.
By the same logic of Object removal null is removed in both cases in the second output.
So to remove the number 3 which is an object we will explicitly need to pass 3 as an object.
And that can be done by casting or wrapping using the wrapper class Integer.
Eg:
Integer removeIndex = Integer.valueOf("3");
collection.remove(removeIndex);
Related
I have a Collection of elements and a single element. I want my getter function looks a little like this
public List<Element> getElements(boolean includeOtherElement){
if (includeOtherElement){
return elements + otherElement; //Obviously this doesn't work, but I'm looking for something that would work like this
}
return elements;
}
Is there a way I could acheive this behavior in a single line, such as with the Stream API or something similar?
EDIT:
I have since realized that I should not be modifying state in a getter method.
If you don't want to add otherElement to the elements list but still want to return a list, you will have to provide a new list.
Simple way:
public List<Element> getElements(boolean includeOtherElement){
if (includeOtherElement){
List<Element> extendedList = new ArrayList<>(elements);
extendedList.add(otherElement);
return extendedList;
}
return elements;
}
This would create a shallow copy of your list.
If you really want to, you could provide your own list implementation, which delegates the first indices to the elements list and the last index to the otherElement;
Usually you do not want your getter function to be changing variables. I would recommend doing something like temp = elements to return a temp variable, with the extra element added via temp.add(otherElement). This way you will not change your current variable while still eliciting the same behavior.
I'm not sure if this answer will work exactly for you but I think it is a good example of something that is possible with the API. The reason that it may or may not be usable in a particular case is that it depends on the location of the extra element(s) being constant in the list.
Suppose it was the case that you didn't want to create a copy of the list, because you actually wanted whoever calls the getter to be able to modify the list. Or, suppose that you just didn't want to make a defensive copy of the list, perhaps because the list is very, very large. In these cases you don't want to add or remove the extra element from the list, you want to return a view of the list which either includes or excludes the extra element.
This is actually possible with existing API, using the List.subList(int, int) method. subList doesn't create a copy, like e.g. String.substring(...) does. Rather, the returned sublist is mutable (if the original list was), and changes to the sublist show through to the original list.
(Though I should note that the semantics of the sublist become undefined if the backing list is structurally modified, i.e. you shouldn't add/remove to and from the original list while keeping a reference to the sublist. If you change the original list through a reference other than the sublist, you have to create a new sublist.)
So what you could do is keep a complete list with all of the elements, then return a sublist of the range which excludes the element(s) you don't want to return.
This could also be used in conjunction with Collections.unmodifiableList(List) if the code which calls the getter is not supposed to be able to mutate the list.
Here's an example which shows this in action:
import java.util.*;
class Example {
public static void main (String[] args) {
Example e = new Example();
List<Object> sub = e.getList(false);
// sublist is [1, 2, 3]
System.out.printf("sublist is %s%n", sub);
sub.add(0, -1);
sub.add(1, 0);
sub.add(5);
// sublist is [-1, 0, 1, 2, 3, 5]
System.out.printf("sublist is %s%n", sub);
List<Object> all = e.getList(true);
// full list is [element0, -1, 0, 1, 2, 3, 5]
System.out.printf("full list is %s%n", all);
// As a side-note, this sequence of calls is
// e.g. what you aren't supposed to do:
// all.add(something); // <- structural modification
// sub.add(another); // <- this is undefined
// You have to make a new sublist first:
// sub = e.getList(false);
// sub.add(another); // <- this is fine now
}
List<Object> objects = new ArrayList<Object>();
Example() {
objects.add("element0");
objects.add(1);
objects.add(2);
objects.add(3);
}
List<Object> getList(boolean includeElement0) {
if (includeElement0) {
return objects;
} else {
return objects.subList(1, objects.size());
}
}
}
Here is the example on Ideone: http://ideone.com/PloZCh.
Also, while not returning a List, this can be done by returning a Stream:
Stream<E> stream = list.stream();
if (includeOtherElement) {
return Stream.concat(stream, Stream.of(otherElement));
} else {
return stream;
}
I suppose this may be the canonical way to do such a thing in Java 8.
Here's a nice pitfall I just encountered.
Consider a list of integers:
List<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(6);
list.add(7);
list.add(1);
Any educated guess on what happens when you execute list.remove(1)? What about list.remove(new Integer(1))? This can cause some nasty bugs.
What is the proper way to differentiate between remove(int index), which removes an element from given index and remove(Object o), which removes an element by reference, when dealing with lists of integers?
The main point to consider here is the one #Nikita mentioned - exact parameter matching takes precedence over auto-boxing.
Java always calls the method that best suits your argument. Auto boxing and implicit upcasting is only performed if there's no method which can be called without casting / auto boxing.
The List interface specifies two remove methods (please note the naming of the arguments):
remove(Object o)
remove(int index)
That means that list.remove(1) removes the object at position 1 and remove(new Integer(1)) removes the first occurrence of the specified element from this list.
You can use casting
list.remove((int) n);
and
list.remove((Integer) n);
It doesn't matter if n is an int or Integer, the method will always call the one you expect.
Using (Integer) n or Integer.valueOf(n) is more efficient than new Integer(n) as the first two can use the Integer cache, whereas the later will always create an object.
I don't know about 'proper' way, but the way you suggested works just fine:
list.remove(int_parameter);
removes element at given position and
list.remove(Integer_parameter);
removes given object from the list.
It's because VM at first attempts to find method declared with exactly the same parameter type and only then tries autoboxing.
list.remove(4) is an exact match of list.remove(int index), so it will be called. If you want to call list.remove(Object) do the following: list.remove((Integer)4).
Any educated guess on what happens when you execute list.remove(1)? What about list.remove(new Integer(1))?
There is no need to guess. The first case will result in List.remove(int) being called, and the element at position 1 will be removed. The second case will result in List.remove(Integer) being called, and the element whose value is equal to Integer(1) will be removed. In both cases, the Java compiler selects the closest matching overload.
Yes, there is potential for confusion (and bugs) here, but it is a fairly uncommon use-case.
When the two List.remove methods were defined in Java 1.2, the overloads were not ambiguous. The problem only arose with the introduction of generics and autoboxing in Java 1.5. In hind-sight, it would have been better if one of the remove methods had been given a different name. But it is too late now.
Note that even if the VM did not do the right thing, which it does, you could still ensure proper behaviour by using the fact that remove(java.lang.Object) operates on arbitrary objects:
myList.remove(new Object() {
#Override
public boolean equals(Object other) {
int k = ((Integer) other).intValue();
return k == 1;
}
}
Simply I did like following as suggested by #decitrig in accepted answer first comment.
list.remove(Integer.valueOf(intereger_parameter));
This helped me. Thanks again #decitrig for your comment. It may help for some one.
Well here is the trick.
Let's take two examples here:
public class ArrayListExample {
public static void main(String[] args) {
Collection<Integer> collection = new ArrayList<>();
List<Integer> arrayList = new ArrayList<>();
collection.add(1);
collection.add(2);
collection.add(3);
collection.add(null);
collection.add(4);
collection.add(null);
System.out.println("Collection" + collection);
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(null);
arrayList.add(4);
arrayList.add(null);
System.out.println("ArrayList" + arrayList);
collection.remove(3);
arrayList.remove(3);
System.out.println("");
System.out.println("After Removal of '3' :");
System.out.println("Collection" + collection);
System.out.println("ArrayList" + arrayList);
collection.remove(null);
arrayList.remove(null);
System.out.println("");
System.out.println("After Removal of 'null': ");
System.out.println("Collection" + collection);
System.out.println("ArrayList" + arrayList);
}
}
Now let's have a look at the output:
Collection[1, 2, 3, null, 4, null]
ArrayList[1, 2, 3, null, 4, null]
After Removal of '3' :
Collection[1, 2, null, 4, null]
ArrayList[1, 2, 3, 4, null]
After Removal of 'null':
Collection[1, 2, 4, null]
ArrayList[1, 2, 3, 4]
Now let's analyze the output:
When 3 is removed from the collection it calls the remove() method of the collection which takes Object o as parameter. Hence it removes the object 3.
But in arrayList object it is overridden by index 3 and hence the 4th element is removed.
By the same logic of Object removal null is removed in both cases in the second output.
So to remove the number 3 which is an object we will explicitly need to pass 3 as an object.
And that can be done by casting or wrapping using the wrapper class Integer.
Eg:
Integer removeIndex = Integer.valueOf("3");
collection.remove(removeIndex);
List<Integer> list = Collections.shuffle(list);
This assignment doesn't work. It gives "incompatible types" even though it's clearly a List and .shuffle() takes a List. So why won't this thing work?
Check the javadoc. Collections.shuffle has a void return type. Create your List first, then shuffle. For example:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Collections.shuffle(list);
System.out.println("Shuffled List: " + list);
There are actually two issues:
You cannot access a variable before you initialize/declare it, even the code List list = new ArrayList(list); will not work, because the right operand of the assignment is parsed before the left operand.
As mentioned by #Reimeus Collections.shuffle() has a void return type, thus you cannot assign it (the "result") to a variable. The method shuffle() changes the same list given as input (might be not intuitive for functional programmers).
I have an ArrayList, which includes a number of items I want to remove. I have the ids of the items to remove stored in another list. Figured the following code should work trivially, but for some reason, the remove() calls are returning a false value:
ArrayList<Integer> toRemove = new ArrayList<Integer>();
ArrayList<JCheckBox> al = new ArrayList<JCheckBox>();
/* Code that adds a bunch of items to al, and a few integers to toRemove */
System.out.println("Size before removing: " + al.size());
for (int i = toRemove.size() - 1; i >= 0; i--) {
System.out.println("Removing id: " + toRemove.get(i) + ": ");
System.out.println(al.get(toRemove.get(i)));
System.out.println(al.remove(toRemove.get(i)));
}
System.out.println("Size after removing: " + al.size());
I'd get it if the get() call also returned a false value, but it does actually return the object in question. What am I missing here?
The output of the code above:
Size before removing: 3
Removing id: 2:
javax.swing.JCheckBox[...]
false
Size after removing: 3
My guess is you are having a problem with the fact that remove() is overloaded with both int and Object, while get() only takes an int. Try remove(toRemove.get(i).intValue()).
remove(Object) from AbstractCollection will search through the list and remove the given object, which won't be there because you are sending it an Integer and the list only has JCheckBoxs. You are trying to call remove(int), but because you are giving it an Integer, the Object overload is called instead. By converting the Integer to an int, you avoid this problem
Also, can you always be sure the Id in toRemove always equals the index? If toRemove is not in greatest to least order, it won't be.
There are two problems with your code.
First, the wrong "toRemove" method is getting invoked. When you call "toRemove.get(i)", the return value is autoboxed into a java.lang.Integer, instead of an int. Therefore, java.util.List#remove(Object) is called instead of java.util.List#remove(int). It's trying to remove an Integer object, and returns false.
If you cast the Integer to an int, the desired method will get invoked.
Second problem: each time you remove an element of the list, the indexes of all subsequent elements change, as those elements are "shifted" down. There are several ways to get around this. One is to sort your list of indexes in descending order. Another would be to use a set of indexes, create a new array, and copy into the new array only those elements whose index is not in the set.
javax.swing.JCheckBox[...]?
Also, try remove elements by Objects value, like here:
http://www.easywayserver.com/blog/java-remove-element-in-arraylist/
If I run this operation on List<Integer> for example, it works as expected (removes first 5 elements), but when I run it on a list of my objects, nothing happens (list stays the same).
list.subList(0, 5).clear();
My class is a pojo that doesn't implement equals or hashCode, if that matters.
UPDATE:
The implementation I am using is ArrayList, which is returned from Hibernate query. There is nothing to show, really. Sublist doesn't return an empty list.
Here is an example for those who don't beleive it works on a list of Integers:
List<Integer> testList = new ArrayList<Integer>();
for(int i=0;i<10;i++) {
testList.add(i);
}
testList.subList(0, 5).clear();
for(int i=0;i<testList.size();i++) {
System.out.print(testList.get(i)+" ");
}
The result is 5 6 7 8 9
UPDATE2: Actually everything is working as expected, don't know how I couldn't see that (got confused by numbers of results). Sorry for false alarm :) This question could be deleted.
It works on my machinetm
import java.util.*;
import static java.lang.System.out;
class SubListExample {
public static void main( String [] args ) {
List<RandomObject> testList = new ArrayList<RandomObject>();
for(int i=0;i<10;i++) {
testList.add( new RandomObject() );
}
System.out.println( "Before: " + testList );
testList.subList(0, 5).clear();
System.out.println( "After: "+ testList );
}
}
class RandomObject {
static Random generator = new Random();
int id = generator.nextInt(100);
public String toString(){
return "ro("+id+")";
}
}
Produces:
$ java SubListExample
Before: [ro(68), ro(97), ro(48), ro(45), ro(43), ro(69), ro(45), ro(8), ro(88), ro(40)]
After: [ro(69), ro(45), ro(8), ro(88), ro(40)]
So, the problem is not in ArrayList nor in your objects.
I don't think Hibernate returns a plain old ArrayList ( may be it does )
Try printing
System.out.println( "That hibernate list.class.name = "
+ listReturnedByHibernate.getClass().getName() );
And let us know if it is in fact an ArrayList
edit - Looks like I was wrong, but leaving my original answer here anyway:
Are you sure that it works with a List<Integer>? It shouldn't.
The method subList() returns a separate List. If you remove elements from that list, it shouldn't affect the original list. The API docs for List.subList() say this:
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive. (If fromIndex and toIndex are equal, the returned list is empty.) The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
Clearing a list is not a non-structural change; only changing the elements in the list are non-structural changes.
This has nothing to do with whether your POJO has equals or hashCode methods or not.
edit - I just tried it out with an ArrayList and it does work (not only with Integer, but also with my own object as a list element).
Two things I can think of are:
list.sublist(0, 5) returns an empty list, therefore .clear() does nothing.
Not sure of the inner workings of the List implementation you're using (ArrayList, LinkedList, etc), but having the equals and hashCode implemented may be important.
I had a simiarl issue with Maps, where HashMap definitely needs the hashCode implementation.
Have you tried creating a List of your objects manually and doing the same thing (without Hibernate)? It seems possible to me that this has to do with Hibernate's lazy loading of data... if you haven't read the data in the returned List, it may not have been loaded yet (since sublists themselves are just views). In that case, it's possible clear would do nothing.
Is it possible that the List returned from Hibernate is not modifiable? i.e. wrapped by Collections.unmodifiableList()