Search code examples
javaarraylisthashmap

whty is my hashmap adding duplicate details


I have this block of code.

I need to return hashmap that has the gender as key,and another hashmaap value the second hash map has yearofbirth as key and arraylist of all people with that saame yearofbear in it .

wrote this code but when i print it prints duplicates of the same customer and employees

public HashMap<Gender, HashMap <Integer, ArrayList<Person>>> personsByGenderAndYearOfBirth() {
    ArrayList<Person> Males=new ArrayList<Person>();
    ArrayList<Person> Females=new ArrayList<Person>();
    HashMap<Integer,ArrayList<Person>> AccBirthDateMale=new HashMap<>();
    HashMap<Integer,ArrayList<Person>> AccBirthDateFemale=new HashMap<>();
    HashMap<Gender, HashMap<Integer,ArrayList<Person>>> AccGender=new HashMap<>();
    for(HashMap.Entry<String, Customer> entry : allCustomers.entrySet())
    {
        {
            switch(entry.getValue().getGender())
            {
            case M:
                Males.add(entry.getValue());
                AccBirthDateMale.put(entry.getValue().getYearOfBirth(),Males);
                break;
            case F:

                Females.add(entry.getValue());
                AccBirthDateFemale.put(entry.getValue().getYearOfBirth(),Females);
                break;
            }
        }
    }
    AccGender.put(Gender.F, AccBirthDateFemale);
    AccGender.put(Gender.M, AccBirthDateMale);
    for(HashMap.Entry<String, Employee> entry : allEmployees.entrySet())
    {
        switch(entry.getValue().getGender())
        {
            case M:
                Males.add(entry.getValue());
                AccBirthDateMale.put(entry.getValue().getYearOfBirth(),Males);
                break;
            case F:
                Females.add(entry.getValue());
                AccBirthDateFemale.put(entry.getValue().getYearOfBirth(),Females);
                break;
        }
    }
    AccGender.put(Gender.F, AccBirthDateFemale);
    AccGender.put(Gender.M, AccBirthDateMale);
    System.out.println(AccBirthDateMale.toString());
    System.out.println(AccGender.toString());
    return AccGender;
}

Solution

  • Problem

    The issue is that you add a person to its gender list, no matter the year, then use this list of the person's year, so every year ends up pointing to the same and unique genderlist

    Males.add(entry.getValue());
    AccBirthDateMale.put(entry.getValue().getYearOfBirth(), Males);
    

    Fix

    You add the outer mapping directly at initialisation of the resulting map

    Iterate directly over the values of the map, then you gendered map per year, and add the mapping if it's missing, add the person to its year, and finally year

    Map<Gender, Map<Integer, List<Person>>> result = Map.of(Gender.M, accBirthDateMale, Gender.F, accBirthDateFemale);
    
    for (Person person : allCustomers.values()) {
        switch (person.getGender()) {
            case M:
                if(!accBirthDateMale.containsKey(person.getYearOfBirth())){
                    accBirthDateMale.put(person.getYearOfBirth(), new ArrayList<>());
                }
                accBirthDateMale.get(person.getYearOfBirth()).add(person);
                break;
            case F:
                if(!accBirthDateFemale.containsKey(person.getYearOfBirth())){
                    accBirthDateFemale.put(person.getYearOfBirth(), new ArrayList<>());
                }
                accBirthDateFemale.get(person.getYearOfBirth()).add(person);
                break;
        }
    }
    result.put(Gender.M, accBirthDateMale);
    result.put(Gender.F, accBirthDateFemale);
    

    Improve

    The previous code is quite verbose, and map offers nice methods to handle the missing-key case (computeIfAbsent)

    public static Map<Gender, Map<Integer, List<Person>>> personsByGenderAndYearOfBirth() {
        Map<Gender, Map<Integer, List<Person>>> result = Map.of(Gender.M, new HashMap<>(), Gender.F, new HashMap<>());
        for(Collection<? extends Person> people : List.of(allCustomers.values(), allEmployees.values())){
            for (Person person : people) {
                result.get(person.getGender()).computeIfAbsent(person.getYearOfBirth(), k -> new ArrayList<>()).add(person);
            }
        }
        return result;
    }
    

    Improve bis

    Using Streams

    public static Map<Gender, Map<Integer, List<Person>>> personsByGenderAndYearOfBirthBis() {
        return Stream.of(allCustomers.values(), allEmployees.values())
                .flatMap(Collection::stream)
                .collect(Collectors.groupingBy(Person::getGender, Collectors.groupingBy(Person::getYearOfBirth, Collectors.toList())));
    }