[java] Java method: Finding object in array list given a known attribute value

I have a couple of questions actually.

I have a class Dog with the following instance fields:

private int id;
private int id_mother;
private int id_father;
private String name="";
private String owner="";
private String bDate="";

I also have a class Archive which can instantiate Dog and put Dog objects into an ArrayList.

I am trying to write a method in Archive which takes an integer as ID and looks through the ArrayList, and returns the object containing that ID.

private Dog getDog(int id){
    Dog dog = new Dog();
    int length=getSize();
    int i=0;

    dog=al.get(i);
    i++;

    while(dog.getId()!=id && i<length)
        dog=al.get(i);
        i++;

    if(dog.getId()!=id)
        dog=null;
    return dog;
}//end getDog

There are two problems with this method (the other methods I use work). First of all it's not working, and I can't see why. I'm while-looping through (potentially) all the objects in the arraylist, for then after the loop is finished, checking whether the loop finished because it ran out of objects to search through, or because it found an object with the given ID. Secondly, that seems like an immensely time-consuming process. Is there some way to speed this up?

This question is related to java arraylist

The answer is


I was interested to see that the original poster used a style that avoided early exits. Single Entry; Single Exit (SESE) is an interesting style that I've not really explored. It's late and I've got a bottle of cider, so I've written a solution (not tested) without an early exit.

I should have used an iterator. Unfortunately java.util.Iterator has a side-effect in the get method. (I don't like the Iterator design due to its exception ramifications.)

private Dog findDog(int id) {
    int i = 0;
    for (; i!=dogs.length() && dogs.get(i).getID()!=id; ++i) {
        ;
    }

    return i!=dogs.length() ? dogs.get(i) : null;
}

Note the duplication of the i!=dogs.length() expression (could have chosen dogs.get(i).getID()!=id).


Assuming that you've written an equals method for Dog correctly that compares based on the id of the Dog the easiest and simplest way to return an item in the list is as follows.

if (dogList.contains(dog)) {
   return dogList.get(dogList.indexOf(dog));
}

That's less performance intensive that other approaches here. You don't need a loop at all in this case. Hope this helps.

P.S You can use Apache Commons Lang to write a simple equals method for Dog as follows:

@Override
public boolean equals(Object obj) {     
   EqualsBuilder builder = new EqualsBuilder().append(this.getId(), obj.getId());               
   return builder.isEquals();
}

I solved this using java 8 lambdas

int dogId = 2;

return dogList.stream().filter(dog-> dogId == dog.getId()).collect(Collectors.toList()).get(0);

If you have to get an attribute that is not the ID. I would use CollectionUtils.

Dog someDog = new Dog();
Dog dog = CollectionUtils(dogList, new Predicate() {

@Override
public boolean evaluate(Object o)
{
    Dog d = (Dog)o;
    return someDog.getName().equals(d.getName());
}
});

You have to loop through the entire array, there's no changing that. You can however, do it a little easier

for (Dog dog : list) {
  if (dog.getId() == id) {
    return dog; //gotcha!
  }
}
return null; // dog not found.

or without the new for loop

for (int i = 0; i < list.size(); i++) {
  if (list.get(i).getId() == id) {
    return list.get(i);
  }
}

To improve performance of the operation, if you're always going to want to look up objects by some unique identifier, then you might consider using a Map<Integer,Dog>. This will provide constant-time lookup by key. You can still iterate over the objects themselves using the map values().

A quick code fragment to get you started:

// Populate the map
Map<Integer,Dog> dogs = new HashMap<Integer,Dog>();
for( Dog dog : /* dog source */ ) {
    dogs.put( dog.getId(), dog );
}

// Perform a lookup
Dog dog = dogs.get( id );

This will help speed things up a bit if you're performing multiple lookups of the same nature on the list. If you're just doing the one lookup, then you're going to incur the same loop overhead regardless.


List<YourClass> list = ArrayList<YourClass>();


List<String> userNames = list.stream().map(m -> m.getUserName()).collect(Collectors.toList());

output: ["John","Alex"]