
本教程详细阐述了如何在java中为推荐系统构建一个非加权图。内容涵盖了从csv文件加载人员和活动数据,将数据存储为对象列表,以及基于共同社区、学校或雇主来定义“密切联系人”关系并构建图结构。同时,文章强调了隐私设置在推荐逻辑中的应用,并提供了具体的代码示例和实现建议,旨在帮助开发者有效组织和处理复杂的人际关系数据。
在构建基于人际关系的推荐系统时,图数据结构是一种极其强大且直观的工具。它能够有效地表示实体(如人)及其之间的复杂关系。例如,在一个联系人推荐系统中,我们可以将每个人视为图中的一个“节点”(Vertex),而他们之间的“密切联系”则可以表示为连接这些节点的“边”(Edge)。本教程将指导您如何从原始数据开始,逐步构建一个非加权图,以实现基于共同社区、学校或雇主以及隐私设置的联系人推荐功能。
在处理数据之前,首先需要定义相应的数据模型来封装人员和活动信息。这将有助于我们以面向对象的方式组织数据。
Person 类应包含所有与个人相关的信息,包括用于判断“密切联系人”的属性以及隐私设置。
public class Person {
private String firstname;
private String lastname;
private String phone;
private String email;
private String community;
private String school;
private String employer;
private String privacy; // "N" for no privacy, "Y" for privacy
// 构造函数
public Person(String firstname, String lastname, String phone, String email,
String community, String school, String employer, String privacy) {
this.firstname = firstname;
this.lastname = lastname;
this.phone = phone;
this.email = email;
this.community = community;
this.school = school;
this.employer = employer;
this.privacy = privacy;
}
// Getters
public String getFirstname() { return firstname; }
public String getLastname() { return lastname; }
public String getCommunity() { return community; }
public String getSchool() { return school; }
public String getEmployer() { return employer; }
public String getPrivacy() { return privacy; }
public boolean hasPrivacyEnabled() { return "Y".equalsIgnoreCase(privacy); }
// 为了在Map或Set中正确使用Person对象,需要重写equals和hashCode方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
// 假设firstname和lastname的组合唯一标识一个人
return firstname.equalsIgnoreCase(person.firstname) &&
lastname.equalsIgnoreCase(person.lastname);
}
@Override
public int hashCode() {
return (firstname.toLowerCase() + lastname.toLowerCase()).hashCode();
}
@Override
public String toString() {
return firstname + " " + lastname + " (Community: " + community + ", School: " + school + ", Employer: " + employer + ", Privacy: " + privacy + ")";
}
// Setters (根据需要添加)
}Activity 类用于存储个人的活动信息。
立即学习“Java免费学习笔记(深入)”;
public class Activity {
private String firstname;
private String lastname;
private String activity;
// 构造函数
public Activity(String firstname, String lastname, String activity) {
this.firstname = firstname;
this.lastname = lastname;
this.activity = activity;
}
// Getters
public String getFirstname() { return firstname; }
public String getLastname() { return lastname; }
public String getActivity() { return activity; }
@Override
public String toString() {
return firstname + " " + lastname + ": " + activity;
}
// Setters (根据需要添加)
}从CSV文件读取数据是构建系统的第一步。关键在于将读取到的每一行数据解析成对应的对象,并将其存储在一个集合中,以便后续处理。
原始代码中读取数据后并未存储对象,导致数据丢失。以下是改进后的 InfoReader 类,它将解析后的 Person 和 Activity 对象分别存储到 ArrayList 中。
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class InfoReader {
private List<Person> persons = new ArrayList<>();
private List<Activity> activities = new ArrayList<>();
public void readInfo() {
// 读取人员数据
try {
String fileLocation = "SamplefilePersons2022Oct31text.csv"; // 假设文件在项目根目录或指定路径
File personListFile = new File(fileLocation);
Scanner personScanner = new Scanner(personListFile);
while (personScanner.hasNextLine()) {
String nextLine = personScanner.nextLine();
String[] personComponents = nextLine.split(",");
// 确保数据完整性,防止数组越界
if (personComponents.length >= 8) {
String firstname = personComponents[0].trim();
String lastname = personComponents[1].trim();
String phone = personComponents[2].trim();
String email = personComponents[3].trim();
String community = personComponents[4].trim();
String school = personComponents[5].trim();
String employer = personComponents[6].trim();
String privacy = personComponents[7].trim();
Person newPerson = new Person(firstname, lastname, phone, email,
community, school, employer, privacy);
persons.add(newPerson); // 存储Person对象
} else {
System.err.println("Warning: Malformed person data line skipped: " + nextLine);
}
}
personScanner.close();
} catch (FileNotFoundException e) {
System.err.println("Person file not found: " + e.getMessage());
throw new RuntimeException("Error reading person data.", e);
}
// 读取活动数据
try {
String fileLocation = "SamplefileActivities2022Oct31text.csv"; // 假设文件在项目根目录或指定路径
File activityListFile = new File(fileLocation);
Scanner activityScanner = new Scanner(activityListFile);
while (activityScanner.hasNextLine()) {
String nextLine = activityScanner.nextLine();
String[] activityComponents = nextLine.split(",");
// 确保数据完整性
if (activityComponents.length >= 3) {
String firstname = activityComponents[0].trim();
String lastname = activityComponents[1].trim();
String activity = activityComponents[2].trim();
Activity newActivity = new Activity(firstname, lastname, activity);
activities.add(newActivity); // 存储Activity对象
} else {
System.err.println("Warning: Malformed activity data line skipped: " + nextLine);
}
}
activityScanner.close();
} catch (FileNotFoundException e) {
System.err.println("Activity file not found: " + e.getMessage());
throw new RuntimeException("Error reading activity data.", e);
}
}
public List<Person> getPersons() {
return persons;
}
public List<Activity> getActivities() {
return activities;
}
}注意事项:
有了加载并存储的 Person 对象列表,我们现在可以着手构建图了。对于非加权图,邻接列表(Adjacency List)是一种非常高效且常用的表示方法,尤其适用于稀疏图(即边相对较少的图)。
我们将使用 Map<Person, Set<Person>> 来表示图。其中,Map的键是图中的一个 Person 节点,而值是一个 Set,包含与该键 Person 节点有“密切联系”的所有其他 Person 节点。使用 Set 可以自动处理重复的联系,确保每个联系人只出现一次。
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class RecommendationGraph {
private Map<Person, Set<Person>> adjacencyList;
public RecommendationGraph() {
this.adjacencyList = new HashMap<>();
}
/**
* 构建图,根据“密切联系人”规则和隐私设置建立边。
* 密切联系人定义:共享相同的社区、学校或雇主。
* 隐私规则:如果某人设置了隐私,则不向其发送推荐(即不建立指向TA的边)。
* @param persons 所有人员列表
*/
public void buildGraph(List<Person> persons) {
// 确保所有人都作为节点存在于图中,即使他们目前没有联系人
for (Person person : persons) {
adjacencyList.putIfAbsent(person, new HashSet<>());
}
// 遍历所有人员,寻找密切联系人
for (int i = 0; i < persons.size(); i++) {
Person personA = persons.get(i);
for (int j = i + 1; j < persons.size(); j++) { // 避免重复比较和自连接
Person personB = persons.get(j);
// 检查是否为密切联系人
boolean isCloseContact =
(personA.getCommunity() != null && !personA.getCommunity().isEmpty() &&
personA.getCommunity().equalsIgnoreCase(personB.getCommunity())) ||
(personA.getSchool() != null && !personA.getSchool().isEmpty() &&
personA.getSchool().equalsIgnoreCase(personB.getSchool())) ||
(personA.getEmployer() != null && !personA.getEmployer().isEmpty() &&
personA.getEmployer().equalsIgnoreCase(personB.getEmployer()));
if (isCloseContact) {
// 如果PersonB没有启用隐私,则PersonA可以向PersonB推荐
if (!personB.hasPrivacyEnabled()) {
adjacencyList.get(personA).add(personB);
}
// 如果PersonA没有启用隐私,则PersonB可以向PersonA推荐
if (!personA.hasPrivacyEnabled()) {
adjacencyList.get(personB).add(personA);
}
}
}
}
}
/**
* 获取指定人员的推荐联系人列表。
* @param person 要获取推荐的人员
* @return 该人员的密切联系人集合
*/
public Set<Person> getRecommendationsFor(Person person) {
return adjacencyList.getOrDefault(person, new HashSet<>());
}
// 打印图结构(可选)
public void printGraph() {
for (Map.Entry<Person, Set<Person>> entry : adjacencyList.entrySet()) {
System.out.println(entry.getKey().getFirstname() + " " + entry.getKey().getLastname() + " -> " + entry.getValue().size() + " contacts:");
for (Person contact : entry.getValue()) {
System.out.println(" - " + contact.getFirstname() + " " + contact.getLastname());
}
}
}
}一旦图构建完成,获取某个人的推荐联系人就变得非常简单,只需查询其在邻接列表中的邻居即可。
public class RecommendationSystemApp {
public static void main(String[] args) {
// 1. 读取数据
InfoReader reader = new InfoReader();
try {
reader.readInfo();
} catch (RuntimeException e) {
System.err.println("Failed to read data: " + e.getMessage());
return;
}
List<Person> allPersons = reader.getPersons();
// List<Activity> allActivities = reader.getActivities(); // 活动数据当前未用于图构建,但可以用于其他推荐维度
// 2. 构建推荐图
RecommendationGraph graph = new RecommendationGraph();
graph.buildGraph(allPersons);
// 3. 获取并打印推荐
System.out.println("--- Recommendation Results ---");
for (Person person : allPersons) {
System.out.println("\nRecommendations for " + person.getFirstname() + " " + person.getLastname() + ":");
Set<Person> recommendations = graph.getRecommendationsFor(person);
if (recommendations.isEmpty()) {
System.out.println(" No close contacts found or all contacts have privacy enabled for this person.");
} else {
for (Person recommendedPerson : recommendations) {
System.out.println(" - " + recommendedPerson.getFirstname() + " " + recommendedPerson.getLastname());
}
}
}
// 示例:查找特定人物的推荐
// 假设我们要找 "Rajay Mccalla" 的推荐
Person targetPerson = null;
for(Person p : allPersons) {
if ("Rajay".equalsIgnoreCase(p.getFirstname()) && "Mccalla".equalsIgnoreCase(p.getLastname())) {
targetPerson = p;
break;
}
}
if (targetPerson != null) {
System.out.println("\n--- Specific Recommendation for " + targetPerson.getFirstname() + " " + targetPerson.getLastname() + " ---");
Set<Person> specificRecs = graph.getRecommendationsFor(targetPerson);
if (specificRecs.isEmpty()) {
System.out.println(" No specific recommendations found.");
} else {
for (Person rec : specificRecs) {
System.out.println(" - " + rec.getFirstname() + " " + rec.getLastname());
}
}
} else {
System.out.println("\nTarget person 'Rajay Mccalla' not found.");
}
}
}// 优化思路示例:按社区分组
Map<String, List<Person>> personsByCommunity = new HashMap<>();
for (Person p : persons) {
if (p.getCommunity() != null && !p.getCommunity().isEmpty()) {
personsByCommunity.computeIfAbsent(p.getCommunity().toLowerCase(), k -> new ArrayList<>()).add(p);
}
}
// 然后只在这些分组内部寻找联系人本教程详细展示了如何从零开始,在Java中构建一个用于推荐系统的非加权图。我们从定义数据模型、安全地加载和存储CSV数据开始,然后重点阐述了如何基于特定规则(共同特征和隐私设置)构建图结构。通过将人员及其关系抽象为图的节点和边,我们能够以清晰、高效的方式实现联系人推荐功能。理解图数据结构及其在实际问题中的应用,是开发复杂推荐系统和社交网络分析工具的关键一步。
以上就是Java中构建非加权图实现联系人推荐系统的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号