MrFisherman
MrFisherman

Reputation: 738

How to merge Maps in Java base on one of the key field?

How to merge two HashMaps<CustomClass key, AnotherCustomClass value> base on one of the CustomClass key inner field? For example I have class Dog with fields String name and String breedingCompany (as keys in Hashmap) and DogStats with field rankingPlace (as value) and I have to import some of Dogs|DogStats from text file. In this case name of the dog and name of breeding company are unique and for Dog only breedingCompany can be changed from file.

Inside program I have existing HashMap:

"Mike", "RosesDogs", 5

"Jack", "RockyDogs", 6

"Annie", "LOLDogs", 7

and I want to update it from text file (and ad some new records if exist):

"Mike", "RosesDogs", 4 (value changed)

"Jack", "BestDogz", 6 (one of key field: "breedingCompany" changed)

"Annie", "LOLDogs", 7 (same as was)

"John", "NewDogsCompany", 1 (new record)

How to update the map and add put new if doesn't exist in current map?

I understand that I have to implement equal and hashcode (only for name) in Dog class but comparing of elements still doesn't work. I don't know how to do it properly.

try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))){
            while ((nextLine = bufferedReader.readLine()) != null) {

                String[] dogsLine = nextLine.split("\\|");
                dogName = dogsLine[0];
                dogBreedingCompany = dogsLine[1];
                dogRankingPlace = Integer.parseInt(dogsLine[2]);

                Dog dog = new Dog(dogName, dogBreedingCompany);
                DogStats dogStats = new DogStats(dogName, dogRankingPlace);

                dogMap.put(dog, dogStats);


                counter++;
            } 

and then for existing map existingDogMap.putAll(InputManager.importDogsFromFile(filename));

Dog.class

import java.util.Objects;

public class Dog {
    private String name;
    private String breedingCompany;

    public Dog(String name, String breedingCompany) {
        this.name = name;
        this.breedingCompany = breedingCompany;
    }

    @Override
    public String toString() {
        return name + "|" + breedingCompany;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Dog dog = (Dog) o;
        return name.equals(dog.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    public String getName() {
        return name;
    }

    public String getBreedingCompany() {
        return breedingCompany;
    }
}

Upvotes: 0

Views: 134

Answers (1)

Andreas Dolk
Andreas Dolk

Reputation: 114757

From the requirements I understand, that you have some dogs with unique names and the company and the ranking can change and should be updated from the data on the file. So, assuming, we have a record

Jack|Rocky Dogs|4

the both of the following entries would update that on the map

Jack|Rocky Dogs|6
Jack|Best Dogs|4

What you see on your code is, that the change of the company does not update the stored values, correct? This is because you use Dog instances as key and two instances are considered equal when the dog names are the same. This means, the following two are equal:

new Dog("Jack", "Rocky Dogs")
new Dog("Jack", "Best Dogs")

Now when the map has an entry where the first dog is a key and now you add an entry for the second dog, then the map keeps the key (new Dog("Jack", "Rocky Dogs")) but updates the value (the new stats). With the unwanted effect: the breeding company wasn't updated, it's still "Rocky Dogs".

How to solve it

The only key that matters is not the Dog instance but the dogs name. So first, I'd create a map like so:

// maps a dog name, which is unique, to dog stats
private Map<String, DogStats> dogStats = new HashMap<>();

And because we lost the breeding company now, Id change the DogStats class to have a dog field instead of dogName

public class DogStats {
  private Dog dog;
  private int rank;

  public DogStats(Dog dog, int rank) {
    this.dog = dog;
    this.rank = rank;
  }

  // getters and setters
}

With this, we don't need to care about special equals and hashcode because the map key is a String.

Upvotes: 1

Related Questions