John Lexus
John Lexus

Reputation: 3636

Add a value to a key in a hashmap if it satisfies a condition

Let us say I have a HashMap that stores a Fruit as its key and an ArrayList of Cars as its value.

A Fruit is an Object with a String name, int height, int width. A Car is an object with a String name, String FruitItRunsOver, int weight.

I am supposed to add Fruits and Cars that run over this Fruit into my HashMap. I am initially given Fruits sequentially and then I am given Cars sequentially.

Now, the problem I am having is that I know which fruit a Car runs over by the FruitItRunsOver member variable which, as you can probably guess, will be the same as the name of a Fruit. Initially, I am given the Fruits, and as I am given them, I put(Fruit, new ArrayList<Car>()).

However, once I get to the cars, I am wondering what is the best way to add them? I am tempted to have something that looks like this:

for (Fruit f : hashmap.keySet()) {
    if (f.getName().equals(car.getFruit())) {
        ArrayList<Car> cars = hashmap.get(f);
        cars.add(car);
        hashmap.put(f, cars)
    }
}

.. But I think that looks egregious. I was thinking I could have the keys, instead of being the Fruits, be the fruit names. But keep in mind I need to have the other member variables of the fruit saved. I will need them later on in the code. So, what, I should have a nested HashMap? If so, how should the structure look like?

Thanks

Edit: I suppose this is my fault for not clarifying. car.getFruit() will return the fruit name FruitItRunsOver, not a Fruit object.

Upvotes: 0

Views: 696

Answers (3)

Jose da Silva Gomes
Jose da Silva Gomes

Reputation: 3974

You can use computeIfAbsent:

The method computeIfAbsent receives as first argument the key and as second argument a Function (function of one argument). This function receives the key and is executed only when the key is not present, then it should return the value. In this case an empty list of cars the when the key does not exist, and the current list of cars when it does exist. Then the new car is added.

Fruit carFruit = getFruitByName(car.getFruit());
hashmap.computeIfAbsent(carFruit, (fruit) -> new ArrayList<>()).add(car);

This way you don't need a for loop, and you are already handling the case when the key does not exists, adding the new list.

To obtain the fruit by name, if you you only have a collection of Fruits you can create the map with java's stream, this map has a string with the name as key and the Fruit itseld as the value:

Map<String, Fruit> fruitByName = getFruits() // however that you get all the fruits 
                                             // e.g. hashmap.keySet()
    .stream()
    .collect(Collectors.toMap(Fruit::getName, Function.identity()));

Having this map Map<String, Fruit>, you could replace the method getFruitByName() with a simple call to fruitByName.get(fruitName).

note: you normally should have the equals() and hashcode() methods defined to use a object as key, the Fruit in this case.

Edit: Using sugestion of Andy Turner

Upvotes: 1

davidxxx
davidxxx

Reputation: 131476

For each Car you add, you iterate on fruits, it is not efficient and in a some way counter intuitive as you have a Map that provides a direct access by key.

It would be more efficient to use as key the same value that the car has as field to represent a fruit. Use the String FruitItRunsOver if it is suitable

Map<String, List<Car>> carsByFruitName = new HashMap<>();
// populate the map with Fruit name and empty ArrayList
..

// for each car to create, retrieve the List of Car from the String name representing the fruit 
List<Car> cars = carsByFruitName.get(car.getFruitItRunsOver());
cars.add(car);   

Upvotes: 2

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726889

Make a Map<String,Fruit> to map fruit names to Fruit objects. This map could be temporary if you plan no further modifications to the fruit-to-cars map, or you could keep it for the duration of the program's run:

Map<String,Fruit> fruitByName = new HashMap<>();
for (Fruit fruit : allFruits) {
    fruitByName.put(fruit.getName(), fruit);
}
Map<Fruit,List<Car>> carByFruit = new HashMap<>();
for (Car car : allCars) {
    Fruit f = fruitByName.get(car.getFruit());
    if (f == null) {
        ... // Throw an exception
    }
    List<Car> cars = carByFruit.get(f);
    if (cars == null) {
        cars = new ArrayList<Car>();
        carByFruit.put(f, cars);
    }
    cars.add(car);
}

You could also make a Map<String,FruitAndItsCars>, with an additional FruitAndItsCars class to combine a Fruit object with a List<Car> object list.

Upvotes: 1

Related Questions