Reputation: 3636
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
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
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
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