views:

1011

answers:

5

I've a Vector of objects, and have to search inside for a random attribute of those objects (For example, a Plane class, a Vector containing Plane; and I've to search sometimes for destination, and others to pilotName).

I know I can traverse the Vector using an Iterator, but I've got stuck at how do I change the comparison made between a String and the attribute on the object. I thought of using switch, but a another opinion would be cool.


Update 1:

The code I've written is something like this (Java n00b alert!):

public int search(String whatSearch, String query){  
    int place = -1;  
    boolean found = false;  
    for ( Iterator<Plane> iteraPlane = this.planes.iterator(); iteraPlane.hasNext() && found == false; ) {  
        Plane temp = (Plane) iteraPlane.next();  
        /* Here is where I have to search for one of many attributes (delimited by whatSearch */ 
    }  
return place;  
}

Seems I've to stick to linear search (and that's a price I've able to pay). Anyway, I was thinking if Java had something like variable variable name (ouch!)

A: 

A simple way is to pass a comparison function to your search routine. Or, if you need more speed, use generics.

Ealdwulf
"if you need more speed, use generics"? What on earth are you on about?
oxbow_lakes
Maybe speed at which the programmer can type the program?
Adam Paynter
+2  A: 

Use Collections.binarySearch and provide a Comparator.

EDIT: This assumes that the Vector is sorted. Otherwise, one has to do a linear search.

quant_dev
note that for this the Vector needs to be sorted using the Compartor
akf
The question is about searching for a *random* attribute so a search that pre-supposes a collection is sorted according to that attribute is pretty useless, isn't it? That would mean is wasn't very random
oxbow_lakes
Then he can sort it first.
quant_dev
But I'd need to sort every time I search for a single attribute
PotterSys
Then do a linear search.
quant_dev
A: 

the equals() method is the best option. For these iterations you could do something like this:

for (Plane plane: planes) {
   if ("JFK".equals(plane.getDestination())) {
      // do your work in here;
   }
}

or you could override the equals() method within Plane to see if the String passed in matches your destination (or pilot). this will allow you to use the indexOf(Object) and indexOf(Object, index) methods on Vector to return you the index(es) of the object(s). Once you have that, you could use Vector.get(index) to return to Object for you.

in Plane.java:

public boolean equals(Object o) {
    return o.equals(getDestination()) ||
           o.equals(getPilot()) ||
           super.equals(o);    

}

there is more work to be done with this option, as you will need to override hashCode() as well (see documentation).

akf
Strictly speaking, if he doesn't put this objects in hashing containers, he doesn't need to worry about hashCode(). But it's good to do it correctly from the start.
quant_dev
i agree, it is better to cover your bases up front.
akf
+3  A: 

I assume that your problem is that you want to have a method that searches for a result based on some property of the collection type. Java is weak on this because it is best expressed in a language which has closures. What you need is something like:

public interface Predicate<T> {
    public boolean evaluate(T t);
}

And then your search method looks like:

public static <T> T findFirst(List<T> l, Predicate<T> p) { //use List, not Vector
    for (T t : l) { if (p.evaluate(t)) return t; }
    return null;
}

Then anyone can use this general-purpose search method. For example, to search for an number in a vector of Integers:

List<Integer> is = ...
findFirst(is, new Predicate<Integer> {
    public boolean evaluate(Integer i) { return i % 2 == 0; }
});

But you could implement the predicate in any way you want; for any arbitrary search

oxbow_lakes
I agree, a Predicate seems ideally suited to this situation.
ColinD
A: 

See @oxbow_lakes above -- I think what you want isn't to pass a String as whatSearch, it's to pass a little snippet of code that knows how to get the property you're interested in. For a less general version:

public static interface PlaneMatcher {
    boolean matches(Plane plane, String query);
}


public int search(PlaneMatcher matcher, String query){  
    int place = -1;  
    boolean found = false;  
    for ( Iterator<Plane> iteraPlane = this.planes.iterator(); iteraPlane.hasNext() && found == false; ) {  
        Plane temp = (Plane) iteraPlane.next();  
        if (matcher.matches(temp, query) {
            found = true;
        }
        place++;
    }  
    return place;  
}

...
// example

int pilotNameIndex = search(new PlaneMatcher() {
        boolean matches(Plane plane, String query) {
            // note: assumes query non-null; you probably want to check that earlier
            return query.equals(plane.getPilotName());
        }
    }, "Orville Wright");

(By the way, if it's the index you're interested in rather than the Plane itself, I wouldn't bother with an Iterator -- just use an old-fashioned for (int i = 0; i < planes.size(); i++) loop, and when you have a match, return i.)

Now, the tricky bit here is if what you have to search for is really identified by arbitrary strings at run-time. If that's the case, I can suggest two alternatives:

  1. Don't store these values as object fields -- plane.pilotName, plane.destination -- at all. Just have a Map<String, String> (or better yet, a Map<Field, String> where Field is an Enum of all the valid fields) called something like plane.metadata.
  2. Store them as object fields, but prepopulate a map from the field names to PlaneMatcher instances as described above.

For instance:

private static final Map<String, PlaneMatcher> MATCHERS = Collections.unmodifiableMap(new HashMap<String, PlaneMatcher>() {{
    put("pilotName", new PlaneMatcher() {
        boolean matches(Plane plane, String query) {
            return query.equals(plane.getPilotName());
        });
    ...
    put("destination", new PlaneMatcher() {
        boolean matches(Plane plane, String query) {
            return query.equals(plane.getDestination());
        });
}}

...

public int search(String whatSearch, String query){  
    PlaneMatcher matcher = MATCHERS.get(whatSearch);
    int place = -1;  
    boolean found = false;  
    for ( Iterator<Plane> iteraPlane = this.planes.iterator(); iteraPlane.hasNext() && found == false; ) {  
        Plane temp = (Plane) iteraPlane.next();  
        if (matcher.matches(temp, query) {
            found = true;
        }
        place++;
    }  
    return place;  
}

Oh, and you might be tempted to use reflection. Don't. :)

David Moles