helloworld helloworld - 2 months ago 9
Java Question

Remove an object from an ArrayList given only one attribute

I have an ArrayList of Items and I want to be able remove one Item from the list by entering only one Item attribute, for example its number (int ItemNumber). I also wanna do the same when I check Item quantities.

These are my

equals()
&
contains()
methods, do I need to make any changes here?

public boolean contains(T anEntry) {
boolean found = false;
for (int index = 0; !found && (index < numberOfEntries); index++) {
if (anEntry.equals(list[index]))
found = true;
}//end for
return found;
} // end contains

public boolean equals(Object object){
Item item = (Item) object;
if (itemNo == item.itemNo)
return true;
return false;
}

Answer

If you change the class Item equals() and compareTo() methods, so that they check only one object field, such as a quantity, it could result in strange behavior in other parts of your application. For example, two items with different itemNo, itemName, and itemPrice, but with the same quantities could be considered equal. Besides, you wouldn't be able to change the comparison attribute without changing the equals() code every time.

Also, creating a custom contains() method makes no sense, since it belongs to the ArrayList class, and not to Item.

If you can use Java 8, a clean way to do it is to use the new Collection's removeIf method:

Suppose you have an Item class with the num and name properties:

class Item {
    final int num;
    final String name;

    Item(int num, String name) {
        this.num = num;
        this.name = name;
    }
}

Given a List<Item> called items and an int variable called number, representing the number of the item you want to remove, you could simply do:

items.removeIf(item -> item.num == number);

If you are unable to use Java 8, you can achieve this by using custom comparators, binary search, and dummy objects.

You can create a custom comparator for each attribute you need to look for. The comparator for num would look like this:

class ItemNumComparator implements Comparator<Item> {
    @Override
    public int compare(Item a, Item b) {
        return (a.num < b.num) ? -1 : ((a.num == b.num) ? 0 : 1);
    }
}

Then you can use the comparator to sort and search for the desired elements in your list:

public static void main(String[] args) {
    List<Item> items = new ArrayList<>();
    items.add(new Item(2, "ball"));
    items.add(new Item(5, "cow"));
    items.add(new Item(3, "gum"));

    Comparator<Item> itemNumComparator = new ItemNumComparator();
    Collections.sort(items, itemNumComparator);

    // Pass a dummy object containing only the relevant attribute to be searched
    int index = Collections.binarySearch(items, new Item(5, ""), itemNumComparator);
    Item removedItem = null;
    // binarySearch will return -1 if it does not find the element.
    if (index > -1) {
        // This will remove the element, Item(5, "cow") in this case, from the list
        removedItem = items.remove(index);
    }
    System.out.println(removedItem);
}

To search for another field like name, for example, you would need to create a name comparator and use it to sort and run the binary search on your list.

Note this solution has some drawbacks though. Unless you are completely sure that the list didn't change since the last sort, you must re-sort it before running the binarySearch() method. Otherwise, it may not be able to find the correct element. Sorting complexity is O(nlogn), so running it multiple times can get quite expensive depending on the size of your list.