views:

127

answers:

5

I have a List of objects. I want to iterate through this list of objects and process some subsets of objects based on a condition and finally create a new list of objects with some processed objects being eliminated.

What is the best way to do this.?

Example:

Actual object : List<Cars>

  1. Iterate through this list and find Cars with same speed.
  2. In that smaller set of Cars, the ones which are of same model are to be eliminated.
  3. Finally after elimination I get the new list.
+4  A: 

The Google Guava libraries have:

Iterables.filter(cars, new Predicate<Car>() {
    @Override
    public boolean apply(Car car) {
         //return true or false depending on whether you 
         // want this car to remain in the list
    }
}

You can also use an intermediate Set - i.e.

 cars = new ArrayList<Car>(new HashSet<Car>(cars));

where you have properly implemented hashCode and equals. This option is viable if this is the identity of your car.


You can also use an iterator:

for (Iterator<Car> it = cars.iterator(); it.hasNext();) {
    Car car = it.next();
    if (conditions here) {
         it.remove();
    }
}

By the way, I'm aware that the above examples don't solve your problem completely - you should still consider what to iterate within the outer loops.

Bozho
I'd recommend referring to Guava rather than google-collections. It's basically just outdated now.
ColinD
I was going to upvote this too, but I don't think this actually has much relevance to what the OP's trying to do, if you read it closely. Your first and third suggestions are applicable for a filtering decision that can be applied on each element independently of other elements; the OP's example doesn't fit that. The second suggestion relies on a condition that probably isn't true.
Mark Peters
well, another iteration within the predicate/outer iteration, if he has to compare to other members of the list should do. O(n^2), but ..
Bozho
+2  A: 

If you are looking to do custom equals comparing, then you should define a Comparator<Car> and then just loop through the Cars.

List<Car> originalList;
Comparator<Car> c = new CarSpeedComparator();
List<Car> result = carFilter(originalList, c);

/// Below is the filter method

public static List<Car> carFilter(List<Car> original, Comparator<Car> comp)
    List<Car> result = new ArrayList<Car>();
    // Process each car
    for (Car car: original) {
        boolean containsC = false;
        // now we check each car in the result
        // to see if we already have an equivalent car
        for (int i = 0; i < result.size(); i++) {
            // if the two cars are equivalent under the rules
            // then we already have that car in the list
            if (comp.compare(result.get(i), car) == 0) {
                containsC = true;
                break;
            }
        }
        // if the result does not contain an equivalent car,
        // add it to the list
        if (!containsC) result.add(car)
    }
    return result;
}

//// Implementation of one of the necessary comparators

public class CarSpeedComparator implements Comparator<Car> {
    public int compare(Car c1, Car c2) {
        return c1.getSpeed() - c2.getSpeed();
    }
}

The resulting list will only contain one car of each speed.

jjnguy
but my processing will happen for a group of objects
@user, so, you want to remove all cars that have the same speed as another car in the list?
jjnguy
@user, see my update
jjnguy
You're using the variable name "c" twice, which makes this a bit harder to read; maybe for each Car car : original?
Dean J
@Dean, yeah, thanks for the feedback. I fixed it.
jjnguy
A: 

If you did this repeatedly on big lists, you'd want to be more efficient. Keep a list of objects, but also keep separate lists for each model of car; Hashtable<String, List> models. That way, you already have the model part done for future sorts. It takes slightly more memory, but notably less time to search through.

Dean J
+1  A: 

It sounds like what you might want to do first is index the cars in your list by speed. Once you've done that, it might be easier to do the rest of the processing you're looking for. Guava's Multimaps are good for this:

ImmutableListMultimap<Integer, Car> speedIndex = Multimaps.index(cars,
    new Function<Car, Integer>() {
      public Integer apply(Car from) {
        return from.getSpeed();
      }
    });

Now speedIndex will be a multimap that lets you do something like this:

for (Integer speed : speedIndex.keySet()) {
  ImmutableList<Car> carsWithSpeed = speedIndex.get(speed);
  // Do stuff
}

This gives you groupings of all cars in the original list that have the same speed. You could then do whatever processing on them you wanted. You might want to index this group of cars by model, giving you groupings of cars that have both the same speed and model. You could then remove those cars from the original list if you wanted. Alternatively, if you don't want to modify the original list at all but just get a copy of the list with a set of cars removed, you could add each car to be removed to a Set, then get the copy with those cars removed like this:

Set<Car> carsToRemove = ...;
List<Car> filteredList = Lists.newArrayList(Iterables.filter(cars,
    Predicates.not(Predicates.in(carsToRemove))));
ColinD
A: 

To me it looks like the OP just wants a unique set of (model,speed) pair. If so, here is a easy way to do it:

class Car {
    private final String model;
    private final int speed;

    public int hashCode(){
         return model.hashCode() + speed;
    }

    public boolean equals(Object obj){
         //check on null/different class omitted.
         Car other = (Car)obj;
         return this.model.equals(obj.model) && this.speed == other.speed;
    }
}

then

 Set<Car> cars = new HashSet<Car>(originalListOfCars);
Enno Shioji