13

I have created a short example of my problem. I'm creating a list of objects anonymously and adding them to an ArrayList. Once items are in the ArrayList I later come back and add more information to each object within the list. Is there a way to extract a specific object from the list if you do not know its index?

I know only the Object's 'name' but you cannot do a list.get(ObjectName) or anything. What is the recommended way to handle this? I'd rather not have to iterate through the entire list every time I want to retrieve one specific object.

public class TestCode{

    public static void main (String args []) {
        Cave cave = new Cave();

        // Loop adds several Parties to the cave's party list
        cave.parties.add(new Party("FirstParty")); // all anonymously added
        cave.parties.add(new Party("SecondParty"));
        cave.parties.add(new Party("ThirdParty"));

        // How do I go about setting the 'index' value of SecondParty for example?
    }
}

class Cave {
    ArrayList<Party> parties = new ArrayList<Party>();
}

class Party extends CaveElement{
    int index;

    public Party(String n){
        name = n;
    }

   // getter and setter methods 

    public String toString () {
        return name;
    }
}


class CaveElement {
    String name = "";
    int index = 0;

    public String toString () {
        return name + "" + index;
    }
}
leigero
  • 3,233
  • 12
  • 42
  • 63

7 Answers7

15

Given the use of List, there's no way to "lookup" a value without iterating through it...

For example...

Cave cave = new Cave();

// Loop adds several Parties to the cave's party list
cave.parties.add(new Party("FirstParty")); // all anonymously added
cave.parties.add(new Party("SecondParty"));
cave.parties.add(new Party("ThirdParty"));

for (Party p : cave.parties) {
    if (p.name.equals("SecondParty") {
        p.index = ...;
        break;
    }
}

Now, this will take time. If the element you are looking for is at the end of the list, you will have to iterate to the end of the list before you find a match.

It might be better to use a Map of some kind...

So, if we update Cave to look like...

class Cave {
    Map<String, Party> parties = new HashMap<String, Party>(25);
}

We could do something like...

Cave cave = new Cave();

// Loop adds several Parties to the cave's party list
cave.parties.put("FirstParty", new Party("FirstParty")); // all anonymously added
cave.parties.put("SecondParty", new Party("SecondParty"));
cave.parties.put("ThirdParty", new Party("ThirdParty"));

if (cave.parties.containsKey("SecondParty")) {
    cave.parties.get("SecondParty").index = ...
}

Instead...

Ultimately, this will all depend on what it is you want to achieve...

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • 1
    I believe this is the most helpful response. I will not be able to use a Map, there's a bit more to this puzzle than I included for this questions for the sake of putting up a SSCCE. Thank you for the answer. – leigero Aug 27 '13 at 03:10
  • 1
    @leigero if you want the best of both worlds, you could use a `HashMap` to store the mapping of `name` -> index.. – Karthik T Aug 27 '13 at 03:15
  • Even if you need the functionality of List for other reasons, you're STILL better off running this as a `LinkedHashMap` behind the scenes and converting it to List as needed. Maps are much more efficient – StormeHawke Aug 27 '13 at 03:15
  • @leigero There are a bunch of tricks you could use if the `ArrayList` was sorted, but that may be beyond your requirements ;) – MadProgrammer Aug 27 '13 at 03:15
  • @StormeHawke That assumes that the OP doesn't need randmon access to the `List`. Still a valid point ;) – MadProgrammer Aug 27 '13 at 03:16
  • Thank you guys. These are the pointers I needed above and beyond the main question. – leigero Aug 27 '13 at 03:16
9

List.indexOf() will give you what you want, provided you know precisely what you're after, and provided that the equals() method for Party is well-defined.

Party searchCandidate = new Party("FirstParty");
int index = cave.parties.indexOf(searchCandidate);

This is where it gets interesting - subclasses shouldn't be examining the private properties of their parents, so we'll define equals() in the superclass.

@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (!(o instanceof CaveElement)) {
        return false;
    }

    CaveElement that = (CaveElement) o;

    if (index != that.index) {
        return false;
    }
    if (name != null ? !name.equals(that.name) : that.name != null) {
        return false;
    }

    return true;
}

It's also wise to override hashCode if you override equals - the general contract for hashCode mandates that, if x.equals(y), then x.hashCode() == y.hashCode().

@Override
public int hashCode() {
    int result = name != null ? name.hashCode() : 0;
    result = 31 * result + index;
    return result;
}
Makoto
  • 104,088
  • 27
  • 192
  • 230
6

If you want to lookup objects based on their String name, this is a textbook case for a Map, say a HashMap. You could use a LinkedHashMap and convert it to a List or Array later (Chris has covered this nicely in the comments below).

LinkedHashMap because it lets you access the elements in the order you insert them if you want to do so. Otherwise HashMap or TreeMap will do.

You could get this to work with List as the others are suggesting, but that feels Hacky to me.. and this will be cleaner both in short and long run.

If you MUST use a list for the object, you could still store a Map of the object name to the index in the array. This is a bit uglier, but you get almost the same performance as a plain Map.

Karthik T
  • 31,456
  • 5
  • 68
  • 87
  • 2
    In particular, you can always use `map.values().toArray(new Party[0])` to dump into an array, and you can use `new ArrayList(map.values())` to dump into a list. – C. K. Young Aug 27 '13 at 03:06
  • 1
    Of all the 5 or 6 answer so far, I like your one the best. It has O(1) lookup and still maintains insertion order. – C. K. Young Aug 27 '13 at 03:07
  • @ChrisJester-Young yup thanks, thats why I suggest `LinkedHashMap`, I should make that explicit – Karthik T Aug 27 '13 at 03:09
  • I can only accept one answer, and I like this one as well. Unfortunately due to the nature of my project I'm kind of stuck with lists so I went with another answer. You still get an up-vote. – leigero Aug 27 '13 at 03:23
  • @leigero that is fine, you accept what makes sense to you. Thanks for the +1. Do give some thought to the hybrid solution I suggest in the end. – Karthik T Aug 27 '13 at 03:36
2

You could use list.indexOf(Object) bug in all honesty what you're describing sounds like you'd be better off using a Map.

Try this:

Map<String, Object> mapOfObjects = new HashMap<String, Object>();
mapOfObjects.put("objectName", object);

Then later when you want to retrieve the object, use

mapOfObjects.get("objectName");

Assuming you do know the object's name as you stated, this will be both cleaner and will have faster performance besides, particularly if the map contains large numbers of objects.

If you need the objects in the Map to stay in order, you can use

Map<String, Object> mapOfObjects = new LinkedHashMap<String, Object>();

instead

StormeHawke
  • 5,987
  • 5
  • 45
  • 73
1

As per your question requirement , I would like to suggest that Map will solve your problem very efficient and without any hassle.

In Map you can give the name as key and your original object as value.

  Map<String,Cave> myMap=new HashMap<String,Cave>();
saurav
  • 3,424
  • 1
  • 22
  • 33
0

I would suggest overriding the equals(Object) of your Party class. It might look something like this:

public boolean equals(Object o){
    if(o == null)
        return false;
    if(o instanceof String)
        return name.equalsIgnoreCase((String)o);
    else if(o instanceof Party)
        return equals(((Party)o).name);
    return false;
}

After you do that, you could use the indexOf(Object) method to retrieve the index of the party specified by its name, as shown below:

int index = cave.parties.indexOf("SecondParty");

Would return the index of the Party with the name SecondParty.

Note: This only works because you are overriding the equals(Object) method.

Josh M
  • 11,611
  • 7
  • 39
  • 49
0

You could simply create a method to get the object by it's name.

public Party getPartyByName(String name) {
    for(Party party : parties) {
        if(name.equalsIgnoreCase(party.name)) {
            return party;
        }
    }
    return null;
}