Java:树状图是一种很好的方法来搜索重复的字符串作为组合键的一部分



>我有一个TreeMap,其中键是基于两个字段的组合键。我希望能够在TreeMap中搜索仅第二个关键元素的匹配项 - 该元素可能有重复项。要解释我正在尝试做什么,请参阅以下内容,

public class CountySchoolsController {
static TreeMap<StudentKey, Student> schoolsMap = new TreeMap<>();
public static void main(String args[]) {
createSchoolsTreeMap();
System.out.println(schoolsMap.get(new StudentKey(1, "Holmes")));
}
private static TreeMap<StudentKey, Student> createSchoolsTreeMap() {
Student s1 = new Student(1, "Sherlock", "Holmes");
Student s2 = new Student(2, "John", "Watson");
Student s3 = new Student(3, "Franklin", "Holmes");
schoolsMap.put(new StudentKey(s1.getSchoolId(), s1.getLastname()), s1);
schoolsMap.put(new StudentKey(s2.getSchoolId(), s2.getLastname()), s2);
schoolsMap.put(new StudentKey(s3.getSchoolId(), s3.getLastname()), s3);
return schoolsMap;
}
}
public class StudentKey implements Comparable<StudentKey>{
int schoolId;
String lastname;
public StudentKey(int id, String lastname){
this.schoolId = id;
this.lastname = lastname;
}
public int getSchoolId() {
return schoolId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
StudentKey that = (StudentKey) o;
return schoolId == that.schoolId &&
Objects.equals(lastname, that.lastname);
}
@Override
public int hashCode() {
return Objects.hash(schoolId, lastname);
}
@Override
public int compareTo(StudentKey o) {
return (this.schoolId + this.lastname).compareTo(o.schoolId + o.lastname);
}
}
public class Student {
int schoolId;
String firstname;
String lastname;
public Student(int schoolId, String firstname, String lastname) {
this.schoolId = schoolId;
this.firstname = firstname;
this.lastname = lastname;
}
public int getSchoolId() {
return schoolId;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
@Override
public String toString() {
return "Student{" +
"schoolId=" + schoolId +
", firstname='" + firstname + ''' +
", lastname='" + lastname + ''' +
'}';
}
}

当我运行此代码时,它运行良好并打印找到的Student

Student{schoolId=1, firstname='Sherlock', lastname='Holmes'}

但是,我希望能够做的是只搜索Holmeslastname并返回由 Id 1 和 3 表示的两条记录。除了像这样进行搜索外,我还需要能够对键的完全匹配进行搜索(如上例所示(。

理想情况下,如果我可以使用wildcard进行schoolId,那就太好了,但我认为这是不可能的。

我可以返回键集值并迭代它以仅在lastname上找到匹配项,但我认为这不会非常有效- 如果您不同意,请告诉我,或者这是否是实现它的最佳方式?还是我应该以另一种方式实现这一点?

试试这个。 它流式传输映射的entrySet,仅筛选姓氏,然后映射到与该名称关联的值并将其放入列表中。 为此,我不得不publiclastname领域。 为字段输入getters会很有用。


List<Student> list = schoolsMap.entrySet().stream()
.filter(e -> e.getKey().lastname
.equals("Holmes"))
.map(Entry::getValue)
.collect(Collectors.toList());

list.forEach(System.out::println);

我决定在这方面走得更远一点。 如果在StudentKey类中为所有key属性设置getters,则可以执行以下操作:

获取Holmes的所有姓氏

List<Student> names = getStudentsForKeyAttribute(
StudentKey::getLastName, "Holmes");
names.forEach(System.out::println);

让学生获得id = 3


List<Student> ids = getStudentsForKeyAttribute(StudentKey::getSchoolId, 3);
ids.forEach(System.out.println); 

以下方法接受一个AttributeExtractor函数,以从StudentKey中提取相应的属性,然后根据提供的参数进行筛选。

public <T> List<Student> getStudentsForKeyAttribute(
Function<StudentKey, T> attrExtractor, T keyAttribute) {
return schoolsMap.entrySet().stream()
.filter(e -> attrExtractor.apply(e.getKey())
.equals(keyAttribute))
.map(Entry::getValue)
.collect(Collectors.toList());
}

主要编辑:我添加了数据保留功能。 第一次通过每个属性,它为该属性构建映射并返回请求的值。将来的调用使用现有地图。

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.function.Function;

// Main attribute for lookup
enum Attribute {
LASTNAME, GENDER
}
// subattribute for gender.  For lastname it would jut be a name.
// I decided to use an enum for gender.
enum Gender {
M, F, O
};
public class CompositeSearch {
// Map of map.
// Example:
//   Outer Map is for LastName attribute
//   Inner map as all lists for common last names.  All names are included.
//     I had to use an Object to allow for different types (enums, strings, ints)
Map<Attribute, Map<Object, List<Student>>> studentsByAttribute = new HashMap<>();

// this provides an extractor for each type requested. It just maps the 
// Attribute to the Student Key method call for that type.
Map<Attribute, Function<StudentKey, ?>> extractorsForType = new HashMap<>() {
{
put(Attribute.LASTNAME,
StudentKey::getLastName);
put(Attribute.GENDER, StudentKey::getGender);
}
};
// intiial data base
TreeMap<StudentKey, Student> schoolsMap = new TreeMap<>();
public static void main(String args[]) {
new CompositeSearch().start();
}
public void start() {
createSchoolsTreeMap();
// getting all female students.
List<Student> list = getStudentsForKeyAttribute(
Attribute.GENDER, Gender.F);
list.forEach(System.out::println);
System.out.println();
// getting all students with last name of holmes.
list = getStudentsForKeyAttribute(Attribute.LASTNAME, "Holmes");
list.forEach(System.out::println);
// All maps for Gender and lastnames have been created so 
// the lookups below require two map retrievals.  The attribute and the 
// sub attribute
System.out.println();
list = getStudentsForKeyAttribute(
Attribute.GENDER, Gender.M);
list.forEach(System.out::println);
System.out.println();
list = getStudentsForKeyAttribute(Attribute.LASTNAME, "Watson");
list.forEach(System.out::println);

}
public <T> List<Student> getStudentsForKeyAttribute(
Attribute attr, T keyAttribute) {
@SuppressWarnings("unchecked")
Function<StudentKey, T> extractor = (Function<StudentKey, T>) extractorsForType
.get(attr);
if (!studentsByAttribute.containsKey(attr)) {
// need to create the map.
System.out.println("Building map for all " + attr);
// sub attribute map
Map<Object, List<Student>> subMap = new HashMap<>();
studentsByAttribute.put(attr, subMap);
for (Map.Entry<StudentKey, ?> e : schoolsMap
.entrySet()) {
T subAttribute = extractor.apply(e.getKey());
subMap.compute(subAttribute,
(k, v) -> v == null
?  new ArrayList<>()
: v)
.add((Student)e.getValue());
}
} else {
System.out.println("Using existing map for all " + attr);
}
return studentsByAttribute.get(attr).get(keyAttribute);
}
// from here on out, everything is pretty normal.
private TreeMap<StudentKey, Student> createSchoolsTreeMap() {
List<Student> students = List.of(
new Student(1, "Sherlock", "Holmes",
Gender.M),
new Student(2, "John", "Watson", Gender.M),
new Student(3, "Franklin", "Holmes",
Gender.M),
new Student(4, "Frances", "Holmes",
Gender.F),
new Student(5, "Mary", "Wilson", Gender.F),
new Student(6, "Martha", "Watson",
Gender.F));
for (Student s : students) {
schoolsMap.put(new StudentKey(s), s);
}
return schoolsMap;
}
}
class StudentKey implements Comparable<StudentKey> {
private int schoolId;
private String lastname;
private Gender gender;
public StudentKey(Student student) {
this.schoolId = student.getSchoolId();
this.lastname = student.getLastname();
this.gender = student.getGender();
}
public int getSchoolId() {
return schoolId;
}
public String getLastName() {
return lastname;
}
public Gender getGender() {
return gender;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
StudentKey that = (StudentKey) o;
return schoolId == that.schoolId
&& Objects.equals(lastname, that.lastname);
}
@Override
public int hashCode() {
return Objects.hash(schoolId, lastname);
}
@Override
public int compareTo(StudentKey o) {
return (this.schoolId + this.lastname)
.compareTo(o.schoolId + o.lastname);
}
}
class Student {
int schoolId;
String firstname;
String lastname;
Gender gender;
public Student(int schoolId, String firstname,
String lastname, Gender gender) {
this.schoolId = schoolId;
this.firstname = firstname;
this.lastname = lastname;
this.gender = gender;
}
public int getSchoolId() {
return schoolId;
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
public Gender getGender() {
return gender;
}
@Override
public String toString() {
return "Student{" + "schoolId=" + schoolId
+ ", firstname='" + firstname + '''
+ ", lastname='" + lastname + ''' + '}';
}
}

嗯,过滤很酷,但很慢。因此,如果您想要一些性能 - 让我们添加索引:

Map<String,Set<StudentKey>> lastNamesMap = new HashMap<>();
Set<StudentKey> getByLastName(String lastName) 
return lastNamesMap.containsKey(s1.getLastname()) ? lastNamesMap.get(s1.getLastname()) : Collections.emptySet();  
}
void addStudent(Student s1) {
final String k = s1.getLastname();
Set<StudentKey> keys;
if (lastNamesMap.containsKey(k)) {
keys = lastNamesMap.get(k);
} else {
keys = new TreeSet<>();
}
StudentKey key = new StudentKey(s1.getSchoolId(), s1.getLastname();
keys.add(key);
lastNamesMap.put(k,keys);
schoolsMap.put(key, s1);
}

最新更新