12 Replies Latest reply: Aug 10, 2009 11:24 AM by 796440 RSS

    ArrayList removing wrong object

    807588
      Folks - I've encountered a problem with Java 1.6 ver 13 but can't find the bug reports to see if a) it's been reported or b) if it is fixed in a later version. The problem is that ArrayList is removing the wrong object. If an array contains a set of strings, some of which are duplicate strings but different objects, the removal of an object where the string is duplicated removes the first occurance of the string not the object.

      Any help or pointers are appreciated. Especially a pointer to the bug reports for Java so I can do a search and/or submittal.
      Lori <*>

      The following code produces this output:
      --- After initialization ---
      Index 0 is Test
      Index 1 is Foo
      Index 2 is bar
      Index 3 is Test
      --- After removal ---
      Index 0 is Foo
      Index 1 is bar
      Index 2 is Test
      import java.util.ArrayList;
      public class ArrayListTest extends Object {
        public static void main(String[] args) {
          final String TEST = "Test";
          final ArrayList<Object> list = new ArrayList<Object>();
          list.add(new String(TEST));
          list.add("Foo");
          list.add("bar");
          final Object remove = new String(TEST);
          list.add(remove);    System.out.println("--- After initialization ---");
          for (int i = 0; i < list.size(); i++) {
            System.out.println("Index " + i + " is " + list.get(i));
          }
          list.remove(remove);
          System.out.println("--- After removal ---");
          for (int i = 0; i < list.size(); i++) {
            System.out.println("Index " + i + " is " + list.get(i));
          }
        }
      }
        • 1. Re: ArrayList removing wrong object
          800345
          That is not a bug. That is behavior as defined by the API.

          From [ArrayList#remove(Object)|http://java.sun.com/javase/6/docs/api/java/util/ArrayList.html#remove(java.lang.Object)]
          >
          remove

          public boolean remove(Object o)

          Removes the first occurrence of the specified element from this list, if it is present. If the list does not contain the element, it is unchanged. More formally, removes the element with the lowest index i such that (o==null ? get(i)==null : o.equals(get(i))) (if such an element exists). Returns true if this list contained the specified element (or equivalently, if this list changed as a result of the call).
          >

          Edited by: stevejluke on Aug 8, 2009 10:16 PM
          • 2. Re: ArrayList removing wrong object
            dcminter
            Not a bug. This is the required behaviour. Here's the excerpt from the base Collection interface's contract:

            Removes a single instance of the specified element from this collection, if it is present (optional operation). More formally, removes an element e such that (o==null ? e==null : o.equals(e)), if this collection contains one or more such elements. Returns true if this collection contained the specified element (or equivalently, if this collection changed as a result of the call).

            The equals method is used to identify the object to remove, not reference equality.
            • 3. Re: ArrayList removing wrong object
              807588
              Totally different than expected but it makes some kind of sense. When I think 'objects' I tend to not consider their contents only that objects are discrete and that they are handled that way. This catches me by suprise that lists do not consider them in that way.

              Oh, well. Back to the code.
              • 4. Re: ArrayList removing wrong object
                796440
                Lori_Cook wrote:
                Totally different than expected but it makes some kind of sense. When I think 'objects' I tend to not consider their contents only that objects are discrete and that they are handled that way. This catches me by suprise that lists do not consider them in that way.
                I think it makes more sense to do it this way. Otherwise you'd have to get hold of the exact object to get rid of it. This way, you say, "I want to remove the object that's like this."
                • 5. Re: ArrayList removing wrong object
                  807588
                  while (arraylist.contains(mystring)) {
                      arraylist.remove(mystring);
                  }
                  or
                  for (final Iterator i = arraylist.iterator(); i.hasNext(); ) {
                      final String value = (String) i.next();
                      if (value.equals(mystring)) {
                          i.remove();
                      }
                  }
                  or
                  final Set remove = new HashSet();
                  remove.add(mystring);
                  myarray.removeAll(remove);
                  I like the first one better for readability. The second one is possibly more performant for very large lists, but that's almost certainly not an issue for you. The third is useful if you want to remove a bunch of objects at once.
                  • 6. Re: ArrayList removing wrong object
                    796440
                    es5f2000 wrote:
                    while (arraylist.contains(mystring)) {
                    arraylist.remove(mystring);
                    }
                    I would highly recommend that the OP never user this approach. It may work fine for small lists on a homework assignment, but it performs as O(n * m) I think, where n is the number of elements in the list and m is the number of occurrences of the string.
                    • 7. Re: ArrayList removing wrong object
                      YoungWinston
                      Lori_Cook wrote:
                      Totally different than expected but it makes some kind of sense. When I think 'objects' I tend to not consider their contents only that objects are discrete and that they are handled that way. This catches me by suprise that lists do not consider them in that way.
                      In which case you're wrong on two counts:
                      1. The determining factor is the result of an 'equals()' method, which (unless you've left it up to Object.equals()) will always be looking at the contents.
                      2. In the case of Strings, you have the added problem that Java caches String literals, so two String objects initialized with the same literal (in your case "Test") are not only 'equal()'; they are '*==*'.

                      Winston
                      • 8. Re: ArrayList removing wrong object
                        800345
                        es5f2000, None of these suggestions actually do what the OP wants. The OP wants to remove the first object in the list which has the same identity as (==) the object to be removed. The problem is that the list will remove the first object in the list which has the same content (.equals()) as the one to be removed. Your loops will remove all objects which are equal (.equals()) to the object to be removed.


                        To the OP: Why is removing the first one that is .equals() incorrect? Why do you need to remove the first value which is == instead of .equals()?
                        • 9. Re: ArrayList removing wrong object
                          807588
                          Fail. :-( You're right, I didn't read carefully enough. My bad.
                          • 10. Re: ArrayList removing wrong object
                            807588
                            To answer several questions:
                            a) The code creates different objects containing the same string. Therefore a .equals() catches the first object instead of the 'remove' object. But a conditional (==) will find the second object (the one I want to remove).

                            b) Why I want to find the second object? This simple example does not show the true scope of the real code. In the application the list contains a master list of strings and objects representing some XML. Since <Title> and </Title> wrap around various headers you have multiple instances of the literal string but each instance exists in a seperate object. When adding a new set of XML you just 'list.addAll(nextSet)' and then write out the .toString() of the list contents to generate the XML. But if you want to remove a set of XML that exist in the middle of the listing doing a list.removeAll() removes the incorrect <Title> and </TItle> objects (not to mention other instances of string literals) and you have malformed XML. (I'm not using the JAVA XML documents for other reasons so don't jump on me for not using them - I may move to them later but not right now.)

                            So...I've created my own class that extends ArrayList and overrides the remove, contains, indexOf and lastIndexOf methods to do conditionals instead of equals() for determining which object to remove. It seemed the easiest way to accomplish what is needed for the application.
                            • 11. Re: ArrayList removing wrong object
                              807588
                              Lori_Cook wrote:
                              (I'm not using the JAVA XML documents for other reasons so don't jump on me for not using them - I may move to them later but not right now.)

                              So...I've created my own class that extends ArrayList and overrides the remove, contains, indexOf and lastIndexOf methods to do conditionals instead of equals() for determining which object to remove. It seemed the easiest way to accomplish what is needed for the application.
                              Ick.
                              • 12. Re: ArrayList removing wrong object
                                796440
                                es5f2000 wrote:
                                Ick.
                                ++