通过 java 中的两个属性键将(最小)对象保留在集合中
Keep (minim) object in a collection by two attribute keys in java
例如我有一个包含 3 个对象的列表:
List<Student> studentList= new ArrayList<Student>();
list.add(new Student("name1", 5);
list.add(new Student("name3", 6);
list.add(new Student("name1", 7);
class Student{ String name; Integer grade;}
我的过滤逻辑:如果名称相等,那么我需要过滤掉具有最高等级的对象 - 所以保留最小等级。
我也不知道集合中有重复的名字。
我坚持这个实现:
Set<Student> setStudents= new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student st1, Student st2) {
int compareName= st1.getName().compareTo(st2.getName());
if (compareName== 0){
int compareGrade = st1.getGrade().compareTo(st2.getGrade());
// ?
}
return compareName;
}
});
setStudents.addAll(studentList);
预期输出:
列表
Student("name1", 5)
Student("name3", 6);
谢谢
我会将成绩封装在一个单独的 class 中。另外,我不会使用列表,对于您的用例来说,这是一种低效的存储机制。我会这样做:
public class Grades {
private Map<String, Integer> store = new HashMap<>();
public void storeMinimumGrade(Student student) {
Integer grade = store.get(student.getName());
if(grade == null || student.getGrade() < grade) {
grade.put(name, grade);
}
}
}
以上对你有用吗?
试试这个代码:
List<Student> studentList= new ArrayList<Student>();
studentList.add(new Student("name1", 5));
studentList.add(new Student("name3", 6));
studentList.add(new Student("name1", 7));
和
Set<Student> setStudents= new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student st1, Student st2) {
int compareName= st1.name.compareTo(st2.name);
if (compareName== 0){
int compareGrade = (st1.grade).compareTo(st2.grade);
if(compareGrade==-1){
// compare two student current minimum grade is replaced with old minimum value
st2.grade=st1.grade;
}
// can not add new student to set,
return 0;
}
return compareName;
}
});
setStudents.addAll(studentList);
检查
for (Student element : setStudents) {
System.out.println(element.name);
}
如果你使用Java 8,你可以编写自己的收集器。像这样:
public class MyCollector implements Collector<Student, Map<String, Student>, Set<Student>> {
@Override
public Supplier<Map<String, Student>> supplier() {
return HashMap::new;
}
@Override
public BiConsumer<Map<String, Student>, Student> accumulator() {
return (nameStudentMap, student) -> nameStudentMap.merge(
student.name, student, (student1, student2) -> student1.grade < student2.grade ? student1 : student2
);
}
@Override
public BinaryOperator<Map<String, Student>> combiner() {
return (nameStudentMap, nameStudentMap2) -> {
nameStudentMap2.forEach(
(s, student) -> nameStudentMap.merge(
s, student, (student1, student2) -> student1.grade < student2.grade ? student1 : student2
)
);
return nameStudentMap;
};
}
@Override
public Function<Map<String, Student>, Set<Student>> finisher() {
return nameStudentMap -> new HashSet<>(nameStudentMap.values());
}
@Override
public Set<Characteristics> characteristics() {
return EnumSet.of(Collector.Characteristics.UNORDERED);
}
}
然后,你可以把这个收集器送给一群学生:
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("mike", 5));
studentList.add(new Student("mike", 1));
studentList.add(new Student("jenn", 8));
studentList.add(new Student("jenn", 2));
studentList.add(new Student("alex", 2));
studentList.add(new Student("mike", 3));
Set<Student> res = studentList.stream().collect(new MyCollector());
检查:
res.forEach((Student s) -> {
System.out.println(s.name + " " + s.grade);
});
输出:
jenn 2
mike 1
alex 2
此解决方案比 TreeSet 更快,因为它使用 HashSet 而不是 TreeSet,它完全解决了您的问题。
这是你的学生 class :
public class Student {
private String name;
private int grade;
public Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
public String getName() {
return name;
}
public int getGrade() {
return grade;
}
public void setName(String name) {
this.name = name;
}
public void setGrade(int grade) {
this.grade = grade;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object obj) {
Student external = (Student) obj;
if (external.getGrade() > this.getGrade()) {
external.setGrade(this.getGrade());
return true;
} else {
return true;
}
}
}
这是你的测试:
public class 启动器 {
public static void main (String ... args){
Set<Student> stSet= new HashSet<Student>();
stSet.add(new Student("name1", 7));
stSet.add(new Student("name3", 6));
stSet.add(new Student("name1", 5));
stSet.add(new Student("name1", 5));
stSet.add(new Student("name1", 1));
stSet.add(new Student("name1", 10));
stSet.add(new Student("name1", 2));
stSet.add(new Student("name1", 9));
stSet.add(new Student("name1", 4));
}
}
输出为1,因为在String"name1"中,最小值为1;
如果你想先创建列表然后获取那个集合,这也是可以的:
public class Launcher {
public static void main (String ... args){
List<Student> stList = new ArrayList<Student>();
stList.add(new Student("name1", 7));
stList.add(new Student("name3", 6));
stList.add(new Student("name1", 5));
stList.add(new Student("name1", 5));
stList.add(new Student("name1", 1));
stList.add(new Student("name1", 10));
stList.add(new Student("name1", 2));
stList.add(new Student("name1", 9));
stList.add(new Student("name1", 4));
Set<Student> stSet= new HashSet<Student>(stList);
}
}
这会给你相同的结果!
如果您被允许使用 Java 8,您可以流式传输数据,按名称分组并使用下游 minBy
收集器。
public void test() {
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("name1", 5));
studentList.add(new Student("name3", 6));
studentList.add(new Student("name1", 7));
// Gather min grade of all students.
Map<String, Optional<Student>> minGrade = studentList.stream()
// Group by student name.
.collect(Collectors.groupingBy(Student::getName,
// Get minimum grade.
Collectors.minBy((s1, s2) -> s1.getGrade().compareTo(s2.getGrade())))
);
System.out.println(minGrade);
}
static class Student {
final String name;
final Integer grade;
public String getName() {
return name;
}
public Integer getGrade() {
return grade;
}
private Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
@Override
public String toString() {
return "Student{" + "name=" + name + ", grade=" + grade + '}';
}
}
例如我有一个包含 3 个对象的列表:
List<Student> studentList= new ArrayList<Student>();
list.add(new Student("name1", 5);
list.add(new Student("name3", 6);
list.add(new Student("name1", 7);
class Student{ String name; Integer grade;}
我的过滤逻辑:如果名称相等,那么我需要过滤掉具有最高等级的对象 - 所以保留最小等级。
我也不知道集合中有重复的名字。
我坚持这个实现:
Set<Student> setStudents= new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student st1, Student st2) {
int compareName= st1.getName().compareTo(st2.getName());
if (compareName== 0){
int compareGrade = st1.getGrade().compareTo(st2.getGrade());
// ?
}
return compareName;
}
});
setStudents.addAll(studentList);
预期输出:
列表
Student("name1", 5)
Student("name3", 6);
谢谢
我会将成绩封装在一个单独的 class 中。另外,我不会使用列表,对于您的用例来说,这是一种低效的存储机制。我会这样做:
public class Grades {
private Map<String, Integer> store = new HashMap<>();
public void storeMinimumGrade(Student student) {
Integer grade = store.get(student.getName());
if(grade == null || student.getGrade() < grade) {
grade.put(name, grade);
}
}
}
以上对你有用吗?
试试这个代码:
List<Student> studentList= new ArrayList<Student>();
studentList.add(new Student("name1", 5));
studentList.add(new Student("name3", 6));
studentList.add(new Student("name1", 7));
和
Set<Student> setStudents= new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student st1, Student st2) {
int compareName= st1.name.compareTo(st2.name);
if (compareName== 0){
int compareGrade = (st1.grade).compareTo(st2.grade);
if(compareGrade==-1){
// compare two student current minimum grade is replaced with old minimum value
st2.grade=st1.grade;
}
// can not add new student to set,
return 0;
}
return compareName;
}
});
setStudents.addAll(studentList);
检查
for (Student element : setStudents) {
System.out.println(element.name);
}
如果你使用Java 8,你可以编写自己的收集器。像这样:
public class MyCollector implements Collector<Student, Map<String, Student>, Set<Student>> {
@Override
public Supplier<Map<String, Student>> supplier() {
return HashMap::new;
}
@Override
public BiConsumer<Map<String, Student>, Student> accumulator() {
return (nameStudentMap, student) -> nameStudentMap.merge(
student.name, student, (student1, student2) -> student1.grade < student2.grade ? student1 : student2
);
}
@Override
public BinaryOperator<Map<String, Student>> combiner() {
return (nameStudentMap, nameStudentMap2) -> {
nameStudentMap2.forEach(
(s, student) -> nameStudentMap.merge(
s, student, (student1, student2) -> student1.grade < student2.grade ? student1 : student2
)
);
return nameStudentMap;
};
}
@Override
public Function<Map<String, Student>, Set<Student>> finisher() {
return nameStudentMap -> new HashSet<>(nameStudentMap.values());
}
@Override
public Set<Characteristics> characteristics() {
return EnumSet.of(Collector.Characteristics.UNORDERED);
}
}
然后,你可以把这个收集器送给一群学生:
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("mike", 5));
studentList.add(new Student("mike", 1));
studentList.add(new Student("jenn", 8));
studentList.add(new Student("jenn", 2));
studentList.add(new Student("alex", 2));
studentList.add(new Student("mike", 3));
Set<Student> res = studentList.stream().collect(new MyCollector());
检查:
res.forEach((Student s) -> {
System.out.println(s.name + " " + s.grade);
});
输出:
jenn 2
mike 1
alex 2
此解决方案比 TreeSet 更快,因为它使用 HashSet 而不是 TreeSet,它完全解决了您的问题。
这是你的学生 class :
public class Student {
private String name;
private int grade;
public Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
public String getName() {
return name;
}
public int getGrade() {
return grade;
}
public void setName(String name) {
this.name = name;
}
public void setGrade(int grade) {
this.grade = grade;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object obj) {
Student external = (Student) obj;
if (external.getGrade() > this.getGrade()) {
external.setGrade(this.getGrade());
return true;
} else {
return true;
}
}
}
这是你的测试:
public class 启动器 {
public static void main (String ... args){
Set<Student> stSet= new HashSet<Student>();
stSet.add(new Student("name1", 7));
stSet.add(new Student("name3", 6));
stSet.add(new Student("name1", 5));
stSet.add(new Student("name1", 5));
stSet.add(new Student("name1", 1));
stSet.add(new Student("name1", 10));
stSet.add(new Student("name1", 2));
stSet.add(new Student("name1", 9));
stSet.add(new Student("name1", 4));
}
}
输出为1,因为在String"name1"中,最小值为1;
如果你想先创建列表然后获取那个集合,这也是可以的:
public class Launcher {
public static void main (String ... args){
List<Student> stList = new ArrayList<Student>();
stList.add(new Student("name1", 7));
stList.add(new Student("name3", 6));
stList.add(new Student("name1", 5));
stList.add(new Student("name1", 5));
stList.add(new Student("name1", 1));
stList.add(new Student("name1", 10));
stList.add(new Student("name1", 2));
stList.add(new Student("name1", 9));
stList.add(new Student("name1", 4));
Set<Student> stSet= new HashSet<Student>(stList);
}
}
这会给你相同的结果!
如果您被允许使用 Java 8,您可以流式传输数据,按名称分组并使用下游 minBy
收集器。
public void test() {
List<Student> studentList = new ArrayList<>();
studentList.add(new Student("name1", 5));
studentList.add(new Student("name3", 6));
studentList.add(new Student("name1", 7));
// Gather min grade of all students.
Map<String, Optional<Student>> minGrade = studentList.stream()
// Group by student name.
.collect(Collectors.groupingBy(Student::getName,
// Get minimum grade.
Collectors.minBy((s1, s2) -> s1.getGrade().compareTo(s2.getGrade())))
);
System.out.println(minGrade);
}
static class Student {
final String name;
final Integer grade;
public String getName() {
return name;
}
public Integer getGrade() {
return grade;
}
private Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
@Override
public String toString() {
return "Student{" + "name=" + name + ", grade=" + grade + '}';
}
}