'HashMap with multiple values get.key() error
import java.util.*;
class Student{
final String name;
final String gender;
public String number;
static Map<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();
static ArrayList<String> nameandNumber = new ArrayList<>();
Student(String number, String name, String gender) {
this.name = name;
this.gender = gender;
this.number = number;
nameandNumber.add(this.name);
nameandNumber.add(this.number);
hm.put(this.gender,nameandNumber);
}
void getPersonByGender() {
String[] Liste = hm.get("Man").toArray(new String[0]);
for (int i = 0; i < Liste.length - 1; i += 2) {
System.out.println(Liste[i] + "\t<------>\t" + Liste[i + 1]);
}
}
}
hello guys i am creating a class and this class will return me 10 student information which I will give (according to the difference between men and women). When i try to use getPersonByGender's function this function gives me all students.
Solution 1:[1]
static ArrayList<String> nameandNumber = new ArrayList<>();
new is useful: Count the amount of times your code ever invokes new ArrayList. It's a fairly easy answer: Once. For your entire program.
If you only call new once, that means there is only one list. In the whole system.
No wonder then: This:
nameandNumber.add(this.number);
is called 10 times (because 10 students) for a single 'run' of your app. Thus, that one list you have must therefore have all these numbers added together - that's why you see all the data.
Your code is, unfortunately, layers of bad design decisions.
You can 'fix' the problem (but it'll still be fragile code that is hard to read), or you can 'fix' the design (which is more work).
Fix the problem
Instead of 1 list shared by all students which obviously can't work, you want to call new ArrayList for each student. Get rid of that static single arraylist, and instead make one every time:
Student(String number, String name, String gender) {
this.name = name;
this.gender = gender;
this.number = number;
var nameandNumber = new ArrayList<String>();
nameandNumber.add(this.name);
nameandNumber.add(this.number);
hm.put(this.gender, nameandNumber);
}
Now you call new ArrayList the right number of times throughout one run of your program, for example.
But you're still in trouble here - because you decided to use a List to represent a single idea (a student), you have confused yourself: A given gender maps to multiple students. Given that a single student is represented by a List<String>, multiple students would be a List<List<String>> and, oof, this is getting real complex, real fast.
We could plug away at fixing this further but let's take a step back and fix your design instead!
Fix the design
More generally, java's typing system is highly nominal: Types have names, and the more descriptive the name, the better.
Student is a far better, clearer name than List<String>. How is a reader of your code supposed to know that those List<String> objects specifically are intended to contain precisely 2 strings, the first of which is the student's name, the second of which is their student ID number? It doesn't say that anywhere. If you mess it up you get no compiler errors of any kind.
You have a type, right there, that properly describes that concept: Student!
So why not replace this bad code:
static Map<String, ArrayList<List<String>>> hm = new HashMap<String, ArrayList<List<String>>>();
With this much improved code:
static Map<String, List<Student>> genderMap = new HashMap<>();
It has all sorts of improvements:
- It has a proper name.
hmdoesn't mean anything. - It uses
<>to be shorter - you don't need to repeat that stuff. - It codes to the principle (
List) instead of the concrete class. - It uses nominal types - this maps a gender string to a list of students. And the code reads the same way, that's good.
Putting it together:
class Student {
final String name;
final String gender;
final String number;
static Map<String, List<Student>> genderMap = new HashMap<>();
Student(String number, String name, String gender) {
this.name = name;
this.gender = gender;
this.number = number;
List<Student> studentsForThisGender = genderMap.get(gender);
if (studentsForThisGender == null) {
// There is no list yet; we need to make one.
genderMap.put(gender, studentsForThisGender = new ArrayList<>());
}
studentsForThisGender.add(this);
}
static void getPersonByGender() {
Student[] liste = genderMap.get("Man").toArray(new Student[0]);
for (Student student : liste) {
System.out.println(student.name + "\t<------>\t" + student.number);
}
}
}
Note:
- getPersonByGender is now
static- that's a thing you do to the concept of 'students' not any particula student. - Instead of this nebulous 'print list[0] - you just sorta have to know that's the name', we print
student.namewhich documents itself. - We fixed the problem where you were list-confused.
- If you get a little further along your java studies, that 'get the list of students for a given gender, and make a new list if neccessary' can be put more succintly. The last 4 lines can be reduced to:
genderMap.computeIfAbsent(gender, () -> new ArrayList<>()).add(this);
But I bet that syntax, and what is happening there, hasn't been covered yet in your java course.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | rzwitserloot |
