Problème - J'ai une classe d'étudiants, elle contient le nom, le numéro de rouleau, trois notes de sujet m1, m2, m3 et le total des notes. Je dois trier l'objet étudiant en fonction de leurs notes totales si deux ou plusieurs notes d'élèves sont égales, puis triez-le en fonction de leur nom. Remarque - Je dois le rechercher sur Google, mais je n'obtiens pas la solution attendue dans la question de stackoverflow à l'aide de l'interface Comparable et Comparator.
J'ai créé la classe Studnt
public class StudentData {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("Enetr the number of Student");
int totalStudent = sc.nextInt();
Map<Integer,Student> map = new TreeMap<Integer,Student>();
for(int i =0;i<totalStudent;i++) {
Student ss = new Student();
System.out.println("Enter the Student roll number");
ss.setRollNumber(sc.nextInt());
System.out.println("Enter the Student Name");
ss.setName(sc.next());
System.out.println("Enter the m1 marks ");
ss.setM1(sc.nextInt());
System.out.println("Enetr the m2 marks ");
ss.setM2(sc.nextInt());
System.out.println("Enter the m3 marks ");
ss.setM3(sc.nextInt());
ss.setTotMarks(ss.getM1()+ss.getM2()+ss.getM3());
map.put(ss.getTotMarks(),ss);
ss=null;
}
//stdList.forEach(System.out::print);
for(Map.Entry<Integer,Student> m :map.entrySet()) {
System.out.println(m);
}
}
}
Classe principale
public class Student {
private String name;
private Integer rollNumber;
private int m1;
private int m2;
private int m3;
private int totMarks;
//Getter setter
}
En fait, j'utilise TreeMap pour trier la valeur par clé (le total des marques est la clé dans mon TreeMap). mais deux élèves ou plus ont des notes égales. Ensuite, l'ancien objet étudiant (valeur) remplacé par le nouvel étudiant en raison de la clé non autorisée en double
output
6 = Élève [name = ved, rollNumber = 12, m1 = 2, m2 = 2, m3 = 2, totMarks = 6]
9 = Élève [name = prakash, rollNumber = 56, m1 = 3, m2 = 3, m3 = 3, totMarks = 9]
les seuls totMarks uniques stockés dans la carte
3 Réponses :
Au lieu de stocker les valeurs en tant qu'élève, stockez-les sous forme de carte de (nom, élève), de sorte que lorsqu'un élève avec les mêmes notes est rencontré, il est ajouté à la carte.
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("Enetr the number of Student");
int totalStudent = sc.nextInt();
Map<Integer, Map<String, Student>> map = new TreeMap<>();
for(int i =0;i<totalStudent;i++) {
Student ss = new Student();
System.out.println("Enter the Student roll number");
ss.setRollNumber(sc.nextInt());
System.out.println("Enter the Student Name");
ss.setName(sc.next());
System.out.println("Enter the m1 marks ");
ss.setM1(sc.nextInt());
System.out.println("Enetr the m2 marks ");
ss.setM2(sc.nextInt());
System.out.println("Enter the m3 marks ");
ss.setM3(sc.nextInt());
ss.setTotMarks(ss.getM1()+ss.getM2()+ss.getM3());
Integer key = ss.getTotMarks();
if (map.get(key) == null){ // if this is a new key in the map, then create a new TreeMap and put it
final TreeMap<String, Student> nameAndStudentMap = new TreeMap<>();
nameAndStudentMap.put(ss.getName(), ss);
map.put(key, nameAndStudentMap);
}else { // if the key already existed, then get the map stored and add to it.
map.get(key).put(ss.getName(), ss);
}
}
//stdList.forEach(System.out::print);
for(Map.Entry<Integer,Map<String, Student>> m :map.entrySet()) {
for (Map.Entry<String, Student> nameAndStudent : m.getValue().entrySet()) {
System.out.println(m.getKey() + " = " + nameAndStudent.getValue());
}
}
}
Je pense que cela ne répond pas tout à fait à l'exigence de trier les traverses par nom.
@GMc Selon votre commentaire, je l'ai modifié pour inclure le tri par nom également en utilisant la carte d'arbre comme valeur de la carte externe.
@PariNgang ok je vais vérifier
Ce code peut être hors de portée / excessif à cette fin. Les suggestions de @GMc ou de Tout le monde dans votre commentaire sont la meilleure façon de procéder.
Cela trie par marques, et stocke les entrées dans map à la fois par marques et par nom. Confusion totale.
@ user207421 la carte externe utilise les marques comme clé, tandis que la carte interne utilise le nom comme clé. Puisque le PO voulait une priorité des marques suivies du nom. Dites que 2 élèves 'x' et 'b' obtiennent 11 comme notes totales, 2 autres 'p' et 'q' obtiennent 12 et 'a' obtiennent 13. Le json ressemblerait à ceci -> {"11" : {"b": {"name": "b", "rollNumber": 12, "totMarks": 11}, "x": {" name": "x", "rollNumbe r": 13 , "totMarks": 11} }, "12": {"p": {"name": "p", "rollNumber": 15, "totMarks": 12}, "q": { "name": "q", "rollNumb er": 14, "totMarks": 12}}, "13": {"a": {"name" : "a", "rollNumber ": 22," totMarks ": 13}}}. Cela aide-t-il à clarifier?
Puisque vous ne pouvez pas utiliser le comparateur ou les algorithmes de tri existants, vous devez le faire vous-même. J'ai implémenté une fonction statique lessOrEqual qui accepte 2 instances Student , les compare et renvoie si oui ou non s1 code> est inférieur ou égal à s2 . plus grand (étudiant s1, étudiant s2) qui renvoie vrai SEULEMENT SI s1 est plus grand que s2 . Il peut y avoir de nombreuses façons de faire cela, c'est vraiment à vous car c'est juste une compréhension. La fonction vérifie d'abord les notes, si les notes correspondent, elle vérifiera ensuite le nom et retournera en conséquence.
EDIT: Comme vous pouvez le voir, j'ai remplacé lessOrEqual par plus grand car le tri de sélection que j'utilise doit trouver plus grand . C'est le même effet, je ne l'ai fait que pour une meilleure lisibilité du genre.
Ensuite, j'ai implémenté une autre fonction statique qui accepte ArrayList, la trie et la renvoie triée. L'algorithme de tri utilisé est très basique: le tri par sélection. C'est O (N ^ 2) qui n'est pas efficace, mais je l'ai fait par souci de simplicité dans la démo ci-dessous.
Code:
public boolean larger(Student other){
if(totMarks < other.totMarks) return false;
else if (totMarks > other.totMarks) return true;
// compare names
else return name.compareTo(other.name) > 0;
}
public static ArrayList<Student> sortSelection(ArrayList<Student> list){
for(int i=0; i<list.size(); i++){
for(int j=i+1; j< list.size(); j++){
// comparison way changes accordingly
if(list.get(i).larger(list.get(j))){ // swap
Student temp = list.get(i);
list.set(i, list.get(j));
list.set(j, temp);
}
}
}
return list;
}
Sortie:
Array before sorting: Name: Ved Parkash - Total Marks: 99 Name: Adam Noob - Total Marks: 100 Name: John Smith - Total Marks: 98 Name: Jack Smith - Total Marks: 98 Array after sorting: Name: Jack Smith - Total Marks: 98 Name: John Smith - Total Marks: 98 Name: Ved Parkash - Total Marks: 99 Name: Adam Noob - Total Marks: 100
Remarques:
1) Voir l'ordre d'ajout des étudiants dans la liste, c'est 4,3, 1 puis 2. C'est pour prouver qu'il trie selon le nom lorsque les notes correspondent (Jack Smith vs John Smith).
2) J'ai codé en dur les étudiants pour faire une meilleure démo.
3) Comme vous le remarquerez peut-être, je ne règle aucune des autres variables car la question concerne uniquement le tri, et les seules variables contribuant au tri sont: name et totMarks . Vous devrez faire le reste.
4) J'utilise ArrayList , mais ce n'est pas limité à cela, avec de simples changements, vous pouvez l'utiliser sur un Student [] normal tableau.
5) La fonction plus grande n'a pas besoin d'être statique , vous pouvez en faire une fonction membre et l'utiliser différemment. Par exemple, le code ci-dessus deviendrait:
import java.util.ArrayList;
public class Student {
private String name;
private Integer rollNumber;
private int m1;
private int m2;
private int m3;
private int totMarks;
public static boolean larger(Student s1, Student s2){
if(s1.totMarks < s2.totMarks) return false;
else if (s1.totMarks > s2.totMarks) return true;
// compare names
else return s1.name.compareTo(s2.name) > 0;
}
public static ArrayList<Student> sortSelection(ArrayList<Student> list){
for(int i=0; i<list.size(); i++){
for(int j=i+1; j< list.size(); j++){
if(larger(list.get(i), list.get(j))){ // swap
Student temp = list.get(i);
list.set(i, list.get(j));
list.set(j, temp);
}
}
}
return list;
}
//Getter setter
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getTotMarks(){
return totMarks;
}
public void setTotMarks(int totMarks){
this.totMarks = totMarks;
}
@Override
public String toString(){
return String.format("Name: %20s - Total Marks: %3d", name, totMarks);
}
public static void main(String[] args){
Student s1 = new Student();
Student s2 = new Student();
Student s3 = new Student();
Student s4 = new Student();
s1.setName("John Smith");
s1.setTotMarks(98);
s2.setName("Jack Smith");
s2.setTotMarks(98);
s3.setName("Adam Noob");
s3.setTotMarks(100);
s4.setName("Ved Parkash");
s4.setTotMarks(99);
ArrayList<Student> list = new ArrayList<>();
list.add(s4);
list.add(s3);
list.add(s1);
list.add(s2);
System.out.println("Array before sorting:");
for(int i=0; i<list.size(); i++){
System.out.println(list.get(i).toString());
}
Student.sortSelection(list);
System.out.println("Array after sorting:");
for(int i=0; i<list.size(); i++){
System.out.println(list.get(i).toString());
}
}
}
Dans l'intérêt de garder les choses simples (c'est-à-dire le principe KISS) et d'expliquer mon "indice" relatif à une clé composée, voici l'exemple travaillé.
La "clé" de la solution est de laisser l'arbre trier les données naturellement (pas, à mon humble avis, à ajouter au code le rendant plus complexe en fournissant un tri manuel). Ainsi, la classe d'élève doit renvoyer une clé que l'arborescence peut naturellement trier.
Pour produire le résultat de tri souhaité, la clé de l'arbre est (total des notes, nom de l'élève).
Voici la classe Student révisée (moins les getters et les setters, mais j'ai ajouté un nouveau constructeur pour me faciliter la vie):
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class StudentData {
public static void main(String[] args) {
// Initialise a list of students (as I don't want to rekey all the details each
// time I run the program).
List<Student> studentList = Arrays.asList(
new Student("Fred", 1, 2, 2, 2), /* Score of 6 */
new Student("John", 2, 2, 3, 3), /* Score of 8 */
new Student("Jane", 3, 20, 25, 30), /* Score of 75 */
new Student("Julie", 4, 20, 15, 10) /* Score of 45 */
// add as many new students as you like, and reorder them
// as much as you like to see if there is any difference in the
// result (there shouldn't be).
);
// Note the key of the tree is of type String - not Integer.
// This is the heart of the algorithm, the tree will be "sorted"
// on the natural key provided. This "natural key" is in fact
// a compound key that is generated by combining the total score
// and the student name.
Map<String,Student> map = new TreeMap<String,Student>();
for (Student ss : studentList) {
map.put(ss.getKey(),ss);
}
//stdList.forEach(System.out::print);
for(Map.Entry<String,Student> m :map.entrySet()) {
System.out.println(m);
}
}
}
Remarque strong > il y a une ligne de code commentée dans la méthode getKey avec le commentaire WRONG . Cela se rapporte à mon indice de test avec des scores à un chiffre. Essayez d'échanger les deux lignes de code pour voir le résultat correct et incorrect.
Voici le principal, j'ai supprimé tous les éléments du Scanner - encore une fois pour me faciliter la vie. J'espère que vous pourrez le suivre et le rajouter dans votre boucle d'analyse.
public class Student {
private String name;
private Integer rollNumber;
private int m1;
private int m2;
private int m3;
private int totMarks;
//Getter setter
public Student() {
}
public Student(String name, Integer rollNumber, int m1, int m2, int m3) {
this.name = name;
this.rollNumber = rollNumber;
this.m1 = m1;
this.m2 = m2;
this.m3 = m3;
this.totMarks = m1 + m2 + m3;
}
public String getKey() {
// return String.format("%d-%s", totMarks, name); // WRONG!
return String.format("%04d-%s", totMarks, name); // Right
}
public String toString() {
return String.format("%06d: %s - %d", rollNumber, name, totMarks);
}
}
J'espère que vous conviendrez que c'est une solution plus simple. Il y a aussi un avantage potentiel en termes de performance car les élèves sont triés au fur et à mesure qu'ils sont chargés dans l'arbre (c'est-à-dire une fois). La performance de ce tri est, je pense, log (n). Le tri lors de la récupération sera probablement n log (n) ou pire.
Très intéressant votre principe KISS et aussi votre explication de mon problème de manière simple.
@JonSkeet: Selon votre suggestion, Une nouvelle question posée par moi, discussion précédente sur ce lien stackoverflow.com/questions/8119366/sorting-hashmap-by-value s
Pourquoi n'utilisez-vous pas Comparable et Comparator? Votre question n'a pas vraiment de sens.
@BasilBourque: En fait, c'est ma restriction d'enseignant. Je dois le faire sans Comparable et Comparator
Si
ComparableetComparatorne sont pas disponibles, vous ne pouvez pas utiliser les routines de tri pratiquesArrays.sortetCollections fournies par JDK. trier. Vous devrez rédiger votre propre procédure de tri. Votre question se résume donc à "Comment écrire une procédure de tri personnalisée?"@BasilBourque: pourquoi ma question n'a pas vraiment de sens? le tri n'est pas possible sans utiliser Comparable et Comparator?
@VedPrakash Vous devez modifier votre question pour expliquer qu'il s'agit d'un devoir, sinon cela n'a aucun sens. Le tri est le but même de Comparable et Comparator. Demander à trier sans eux, c'est comme demander comment conduire une voiture sans moteur ni roues.
@KevinAnderson: oui. Question mise à jour avec un tri personnalisé
@BasilBourque: En fait, j'essaye de conduire une voiture avec mon moteur personnalisé (* _ *)
Ce dont vous avez besoin pour rechercher sur Google, c'est donc des "algorithmes de tri". Ou recherchez des algorithmes spécifiques tels que "tri par bulles" (mauvais), "tri par insertion" ou "tri par sélection", (légèrement meilleur), "tri par shell" (bien meilleur), "tri rapide" (meilleur), etc.
@KevinAnderson: J'essaye toujours d'écrire mon tri personnalisé mais je n'arrive pas à résoudre mon problème. C'est trop facile mais je ne sais pas pourquoi je reste coincé à chaque essai (- _ -)
Quel problème cela poserait-il?
@KevinAnderson: je vous mettrai à jour dès que possible
Je pense que la bonne réponse est que cela dépend du sujet que vous étudiez actuellement. Par exemple. s le sujet comment écrire votre propre arbre qui accepte les algorithmes de tri définis par l'utilisateur? Ou comment résoudre les problèmes de manière créative? Par exemple, vous pouvez dériver une clé composite - constituée du total des marques et du nom. Par exemple "6-ved" ou "9-prakash". Je ne vais pas vous donner la réponse complète, mais je vais vous donner un indice. Faites tester les personnes avec des scores> = 10 (par exemple 65) et des scores à un chiffre (par exemple 5 ou 7) et assurez-vous que vous obtenez le bon résultat. Vous pouvez avec ce schéma, mais vous devez réfléchir à la façon dont.
Je viens de réaliser que vous demandez comment effectuer un tri personnalisé sans utiliser les interfaces
ComparatorouComparable. C'est un peu étrange, car les objets de vos élèves ne sont pascomparableset les cartes ne sont généralement pas ordonnées@GMc selon votre indice, je vais essayer de résoudre
Pourquoi n'implémentez-vous pas une méthode statique dans la classe Student:
lessOrEqualqui accepte 2 instances Student et les compare selon vos critères? Ensuite, vous pouvez utiliser cette méthode pour trier une liste d'étudiants en fonction de n'importe quel algorithme de tri (le tri rapide est préférable).@Everyone: OK, j'essaierai de faire selon votre suggestion> (+ _ +) <
@VedPrakash J'ai fait une démo expliquant ce que je voulais dire par mon commentaire initial.