By Yuval Adam


2010-12-26 14:25:35 8 Comments

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.

8 comments

@Pritam Banerjee 2017-08-29 08:12:38

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:

  1. 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.

  2. 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);

@Shylendra Madda 2017-03-08 07:11:38

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.

@Peter Lawrey 2010-12-26 14:31:15

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.

@Yuval Adam 2010-12-26 14:44:29

it would be nice if you could explain why that is the case :) [autoboxing conditions...]

@Peter Lawrey 2010-12-26 14:53:18

By using casting, you ensure the compiler sees the type you expect. In the first case '(int) n' can only be of type int in the second case '(Integer) n' can only be of type Integer. 'n' will be converted/boxed/unboxed as required or you will get a compiler errors if it cannot.

@aka 2010-12-26 14:45:49

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.

@decitrig 2011-04-24 15:34:44

Picking a nit: Integer.valueOf(1) is better practice than new Integer(1). The static method can do caching and such, so you'll get better performance.

@assylias 2013-03-14 18:43:30

Peter Lawrey's proposal is better and avoids unnecessary object creations.

@Mark Peters 2013-03-14 18:47:14

@assylias: Peter Lawrey's proposal does the exact same thing as decitrig's proposal, only less transparently.

@assylias 2013-03-14 19:35:54

@MarkPeters My comment was about new Integer(1), but I agree that Integer.valueOf(1) or (Integer) 1 are equivalent.

@Stephen C 2010-12-26 14:35:04

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.

@user268396 2010-12-26 14:35:28

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;
  }
}

@Erwin Bolwidt 2017-07-24 15:14:37

This "solution" breaks the contract of the equals method, specifically (from the Javadoc) "It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.". As such it is not guaranteed to work on all implementations of List, because any implementation of List is allowed to swap the x and the y in x.equals(y) at will, since the Javadoc of Object.equals says that this should be valid.

@Petar Minchev 2010-12-26 14:31:51

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).

@vikingsteve 2013-08-21 10:01:52

Thanks Petar, a simple (Integer) cast like you wrote above seems to be the easiest approach for me.

@Bram Vanroy 2015-11-15 16:15:21

When using your last approach, it seems to return a boolean. When trying to stack multiple removes I get the error that I cannot call remove on a boolean.

@Nikita Rybak 2010-12-26 14:29:37

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.

Related Questions

Sponsored Content

24 Answered Questions

[SOLVED] How to make a new List in Java

  • 2009-05-13 15:12:45
  • user93796
  • 1789283 View
  • 698 Score
  • 24 Answer
  • Tags:   java list collections

65 Answered Questions

[SOLVED] How do I generate random integers within a specific range in Java?

  • 2008-12-12 18:20:57
  • user42155
  • 3873982 View
  • 3327 Score
  • 65 Answer
  • Tags:   java random integer

34 Answered Questions

[SOLVED] Create ArrayList from array

21 Answered Questions

[SOLVED] How do I call one constructor from another in Java?

  • 2008-11-12 20:10:19
  • ashokgelal
  • 796168 View
  • 2115 Score
  • 21 Answer
  • Tags:   java constructor

16 Answered Questions

[SOLVED] How to convert List<Integer> to int[] in Java?

23 Answered Questions

35 Answered Questions

15 Answered Questions

[SOLVED] Easiest way to convert a List to a Set in Java

  • 2009-09-15 22:02:35
  • OHHAI
  • 498344 View
  • 592 Score
  • 15 Answer
  • Tags:   java collections

26 Answered Questions

[SOLVED] How to get an enum value from a string value in Java?

  • 2009-03-02 22:56:34
  • Malachi
  • 1042364 View
  • 1870 Score
  • 26 Answer
  • Tags:   java enums

13 Answered Questions

[SOLVED] How to convert a Map to List in Java?

Sponsored Content