构建Java推荐系统中的非加权图及关系建模

构建Java推荐系统中的非加权图及关系建模

本文详细阐述如何在java中为推荐系统构建非加权图,以有效管理和识别用户间的复杂关系,如“密切联系人”。文章从数据读取和存储入手,逐步指导如何将人员信息转化为图的节点,并基于共享属性(社区、学校、雇主)定义边,最终形成邻接列表表示的图结构,并整合隐私设置,为后续的推荐逻辑奠定基础。

在构建推荐系统时,有效地表示和管理实体之间的关系至关重要。对于需要基于“密切联系人”概念进行推荐的场景,图数据结构提供了一种直观且强大的建模方式。本文将指导您如何在Java中,从原始文件数据出发,构建一个非加权图来表示人员之间的关系,并集成隐私设置。

1. 数据模型定义

首先,我们需要定义用于存储人员和活动信息的Java类。这些类将作为图的节点和相关属性的载体。

import java.util.Objects;// 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; // "Y" 表示请求隐私,"N" 表示不请求    // 构造函数    public Person() {}    // Getters and Setters    public String getFirstname() { return firstname; }    public void setFirstname(String firstname) { this.firstname = firstname; }    public String getLastname() { return lastname; }    public void setLastname(String lastname) { this.lastname = lastname; }    public String getPhone() { return phone; }    public void setPhone(String phone) { this.phone = phone; }    public String getEmail() { return email; }    public void setEmail(String email) { this.email = email; }    public String getCommunity() { return community; }    public void setCommunity(String community) { this.community = community; }    public String getSchool() { return school; }    public void setSchool(String school) { this.school = school; }    public String getEmployer() { return employer; }    public void setEmployer(String employer) { this.employer = employer; }    public String getPrivacy() { return privacy; }    public void setPrivacy(String privacy) { this.privacy = privacy; }    // 判断是否请求隐私    public boolean requestsPrivacy() {        return "Y".equalsIgnoreCase(privacy);    }    // 重写 equals 和 hashCode 方法,确保在集合(如Map的键)中正确识别Person对象    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        Person person = (Person) o;        return Objects.equals(firstname, person.firstname) &&               Objects.equals(lastname, person.lastname) &&               Objects.equals(email, person.email); // 假设 firstname, lastname, email 组合唯一标识一个人    }    @Override    public int hashCode() {        return Objects.hash(firstname, lastname, email);    }    @Override    public String toString() {        return "Person{" +               "firstname='" + firstname + ''' +               ", lastname='" + lastname + ''' +               ", community='" + community + ''' +               ", school='" + school + ''' +               ", employer='" + employer + ''' +               ", privacy='" + privacy + ''' +               '}';    }}// Activities 类用于存储活动信息 (在本教程中,活动信息不直接用于构建图,但作为原始数据的一部分保留)public class Activities {    private String firstname;    private String lastname;    private String activity;    // 构造函数    public Activities() {}    // Getters and Setters    public String getFirstname() { return firstname; }    public void setFirstname(String firstname) { this.firstname = firstname; }    public String getLastname() { return lastname; }    public void setLastname(String lastname) { this.lastname = lastname; }    public String getActivity() { return activity; }    public void setActivity(String activity) { this.activity = activity; }    @Override    public String toString() {        return "Activities{" +               "firstname='" + firstname + ''' +               ", lastname='" + lastname + ''' +               ", activity='" + activity + ''' +               '}';    }}

注意事项:

Person 类中重写 equals() 和 hashCode() 方法至关重要。当 Person 对象被用作 Map 的键或存储在 Set 中时,这些方法确保了对象的正确比较和唯一性识别。通常,选择一个或多个属性组合作为唯一标识(例如,姓、名和电子邮件)。privacy 属性的 requestsPrivacy() 方法提供了一个便捷的方式来检查隐私状态。

2. 数据读取与存储优化

原始数据文件需要被读取并存储到易于操作的集合中。ArrayList 是一个合适的选择,用于临时存储所有 Person 和 Activities 对象。

立即学习“Java免费学习笔记(深入)”;

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 persons;    private List activities;    public InfoReader() {        this.persons = new ArrayList();        this.activities = new ArrayList();    }    public void ReadInfo() {        // 读取人员数据        try {            // 请根据您的实际文件路径修改            String personFileLocation = "path" + File.separator + "to" + File.separator + "SamplefilePersons2022Oct31text.csv";            File personListFile = new File(personFileLocation);            Scanner personScanner = new Scanner(personListFile);            while (personScanner.hasNextLine()) {                String nextline = personScanner.nextLine();                String[] personComponents = nextline.split(",");                if (personComponents.length >= 8) { // 确保数据完整性                    Person newPerson = new Person();                    newPerson.setFirstname(personComponents[0].trim());                    newPerson.setLastname(personComponents[1].trim());                    newPerson.setPhone(personComponents[2].trim());                    newPerson.setEmail(personComponents[3].trim());                    newPerson.setCommunity(personComponents[4].trim());                    newPerson.setSchool(personComponents[5].trim());                    newPerson.setEmployer(personComponents[6].trim());                    newPerson.setPrivacy(personComponents[7].trim());                    this.persons.add(newPerson); // 将 Person 对象存储到列表中                }            }            personScanner.close(); // 关闭扫描器        } catch (FileNotFoundException e) {            System.err.println("人员文件未找到: " + e.getMessage());            throw new RuntimeException("无法读取人员数据", e);        }        // 读取活动数据        try {            // 请根据您的实际文件路径修改            String activityFileLocation = "path" + File.separator + "to" + File.separator + "SamplefileActivities2022Oct31text.csv";            File activityListFile = new File(activityFileLocation);            Scanner activityScanner = new Scanner(activityListFile);            while (activityScanner.hasNextLine()) {                String nextLine = activityScanner.nextLine();                String[] activityComponents = nextLine.split(",");                if (activityComponents.length >= 3) { // 确保数据完整性                    Activities newActivity = new Activities();                    newActivity.setFirstname(activityComponents[0].trim());                    newActivity.setLastname(activityComponents[1].trim());                    newActivity.setActivity(activityComponents[2].trim());                    this.activities.add(newActivity); // 将 Activities 对象存储到列表中                }            }            activityScanner.close(); // 关闭扫描器        } catch (FileNotFoundException e) {            System.err.println("活动文件未找到: " + e.getMessage());            throw new RuntimeException("无法读取活动数据", e);        }    }    public List getPersons() {        return persons;    }    public List getActivities() {        return activities;    }}

关键改进:

在 InfoReader 类中定义了 persons 和 activities 两个 ArrayList 成员变量。在循环内部,创建 Person 或 Activities 对象后,立即将其添加到对应的列表中。添加了 trim() 方法去除字符串两端的空白,提高数据处理的健壮性。增加了 if (personComponents.length >= X) 检查,避免因数据行不完整而导致的 ArrayIndexOutOfBoundsException。在 try-catch 块中关闭了 Scanner 资源。

3. 图数据结构基础与表示

图由节点(Vertices/Nodes)边(Edges)组成。

在我们的场景中,每个 Person 对象可以被视为一个节点。如果两个人之间满足“密切联系人”的条件(共享社区、学校或雇主),则他们之间存在一条。这是一个非加权图,因为边没有关联的权重(例如,联系的强度)。这是一个无向图,因为如果A是B的密切联系人,那么B也是A的密切联系人。

图的常见表示方法有邻接矩阵和邻接列表。对于稀疏图(边相对较少),邻接列表通常更高效,它使用 Map 来存储每个节点及其相邻节点列表。

import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class Graph {    // 邻接列表:Map 的键是 Person 节点,值是其所有相邻 Person 节点的列表    private Map<Person, List> adjList;    public Graph() {        this.adjList = new HashMap();    }    // 添加一个节点到图中(如果它还不存在)    public void addPerson(Person person) {        adjList.putIfAbsent(person, new ArrayList());    }    // 添加一条边:将 person2 添加到 person1 的邻接列表中,反之亦然(无向图)    public void addRelationship(Person person1, Person person2) {        // 确保两个人都已作为节点存在        addPerson(person1);        addPerson(person2);        // 添加双向关系        if (!adjList.get(person1).contains(person2)) {            adjList.get(person1).add(person2);        }        if (!adjList.get(person2).contains(person1)) {            adjList.get(person2).add(person1);        }    }    // 获取某个人的所有密切联系人    public List getCloseContacts(Person person) {        return adjList.getOrDefault(person, new ArrayList());    }    // 打印图的结构    public void printGraph() {        for (Map.Entry<Person, List> entry : adjList.entrySet()) {            System.out.print("Person: " + entry.getKey().getFirstname() + " " + entry.getKey().getLastname() + " -> ");            for (Person neighbor : entry.getValue()) {                System.out.print(neighbor.getFirstname() + " " + neighbor.getLastname() + ", ");            }            System.out.println();        }    }}

4. 构建人员关系图

现在,我们将读取的数据转换为图结构。核心逻辑是遍历所有人员对,检查他们是否符合“密切联系人”的条件,然后添加相应的边。

import java.util.List;import java.util.Objects; // 用于Objects.equals的空值安全比较public class GraphBuilder {    public static Graph buildRecommendationGraph(List persons) {        Graph graph = new Graph();        // 将所有人员添加到图中作为节点        for (Person p : persons) {            graph.addPerson(p);        }        // 遍历所有人员对,建立密切联系关系        for (int i = 0; i < persons.size(); i++) {            for (int j = i + 1; j < persons.size(); j++) { // 避免重复和自连接                Person p1 = persons.get(i);                Person p2 = persons.get(j);                // 判断是否为密切联系人:共享社区、学校或雇主                boolean isCloseContact = false;                if (p1.getCommunity() != null && !p1.getCommunity().isEmpty() &&                    Objects.equals(p1.getCommunity(), p2.getCommunity())) {                    isCloseContact = true;                }                if (p1.getSchool() != null && !p1.getSchool().isEmpty() &&                    Objects.equals(p1.getSchool(), p2.getSchool())) {                    isCloseContact = true;                }                if (p1.getEmployer() != null && !p1.getEmployer().isEmpty() &&                    Objects.equals(p1.getEmployer(), p2.getEmployer())) {                    isCloseContact = true;                }                if (isCloseContact) {                    graph.addRelationship(p1, p2);                }            }        }        return graph;    }    // 整合隐私设置的推荐生成方法    public static List getRecommendedContacts(Person targetPerson, Graph graph) {        List recommendations = new ArrayList();        // 如果目标人员请求隐私,则不进行任何推荐        if (targetPerson.requestsPrivacy()) {            System.out.println(targetPerson.getFirstname() + " " + targetPerson.getLastname() + " 已请求隐私,不生成推荐。");            return recommendations;        }        // 获取目标人员的所有密切联系人        List closeContacts = graph.getCloseContacts(targetPerson);        // 过滤掉请求隐私的联系人        for (Person contact : closeContacts) {            if (!contact.requestsPrivacy()) {                recommendations.add(contact);            }        }        return recommendations;    }    public static void main(String[] args) {        // 1. 读取数据        InfoReader reader = new InfoReader();        reader.ReadInfo(); // 确保您已修改文件路径        List allPersons = reader.getPersons();        // List allActivities = reader.getActivities(); // 活动数据在本例中不直接用于图构建        // 2. 构建图        Graph recommendationGraph = buildRecommendationGraph(allPersons);        System.out.println("--- 图结构 ---");        recommendationGraph.printGraph();        // 3. 生成推荐        System.out.println("n--- 推荐结果 ---");        if (!allPersons.isEmpty()) {            // 假设我们想为列表中的第一个人生成推荐            Person personToRecommendFor = allPersons.get(0);            List recommendations = getRecommendedContacts(personToRecommendFor, recommendationGraph);            System.out.println("为 " + personToRecommendFor.getFirstname() + " " + personToRecommendFor.getLastname() + " 推荐的联系人:");            if (recommendations.isEmpty()) {                System.out.println("  无推荐或所有密切联系人均已请求隐私。");            } else {                for (Person rec : recommendations) {                    System.out.println("  - " + rec.getFirstname() + " " + rec.getLastname());                }            }            // 示例:为另一个可能请求隐私的人生成推荐            // 假设我们有一个名为 "John Doe" 的人,并且他请求了隐私            Person privacyRequester = new Person();            privacyRequester.setFirstname("John");            privacyRequester.setLastname("Doe");            privacyRequester.setPrivacy("Y"); // 设置为请求隐私            privacyRequester.setCommunity("SomeCommunity"); // 确保equals方法能匹配            privacyRequester.setSchool("SomeSchool");            privacyRequester.setEmail("john.doe@example.com");            // 假设 John Doe 也在 allPersons 列表中,或者我们只是模拟            // 如果不在列表中,getCloseContacts可能返回空,这里仅为演示隐私过滤            // 实际应用中,privacyRequester 应该来自 allPersons 列表            List privacyRecommendations = getRecommendedContacts(privacyRequester, recommendationGraph);            if (privacyRecommendations.isEmpty() && privacyRequester.requestsPrivacy()) {                 // 已经打印了信息,这里不再重复            } else if (!privacyRecommendations.isEmpty()) {                System.out.println("为 " + privacyRequester.getFirstname() + " " + privacyRequester.getLastname() + " 推荐的联系人:");                for (Person rec : privacyRecommendations) {                    System.out.println("  - " + rec.getFirstname() + " " + rec.getLastname());                }            }        } else {            System.out.println("没有读取到人员数据,无法生成推荐。");        }    }}

代码解析:

buildRecommendationGraph 方法负责图的实际构建。它首先将所有 Person 对象添加为图的节点,然后通过嵌套循环遍历所有人员对。Objects.equals() 用于安全地比较字符串属性,即使其中一个为 null 也不会抛出 NullPointerException。getRecommendedContacts 方法演示了如何利用构建好的图来生成推荐。它首先获取目标人员的所有直接密切联系人,然后根据隐私

以上就是构建Java推荐系统中的非加权图及关系建模的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/15124.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
MySQL如何监控数据库运行状态(常用性能指标查看方法)
上一篇 2025年11月26日 09:37:36
《背包英雄》手游入坑指南
下一篇 2025年11月26日 09:39:38

相关推荐

  • 修复Django电商项目中AJAX过滤产品列表图片不显示问题

    在Django电商项目中,当使用AJAX动态加载过滤后的产品列表时,常遇到图片无法正常显示的问题。这通常是由于前端模板中图片加载方式(如data-setbg属性结合JavaScript库)与AJAX动态内容更新机制不兼容所致。解决方案是直接在AJAX返回的HTML中使用标准的标签来渲染图片,确保浏览…

    2026年5月10日
    000
  • Golang JSON序列化:控制敏感字段暴露的最佳实践

    本教程探讨golang中如何高效控制结构体字段在json序列化时的可见性。当需要将包含敏感信息的结构体数组转换为json响应时,通过利用`encoding/json`包提供的结构体标签,特别是`json:”-“`,可以轻松实现对特定字段的忽略,从而避免敏感数据泄露,确保api…

    2026年5月10日
    000
  • 比特币新手教程 比特币交易平台有哪些

    比特币是一种去中心化的数字货币,基于区块链技术实现点对点交易,具有匿名性、有限发行和不可篡改等特点;新手可通过交易所购买,P2P交易获得比特币,常用平台包括Binance、OKX和Huobi;交易流程包括注册账户、实名认证、绑定支付方式、充值法币并下单购买,可选择市价单或限价单;比特币存储方式有交易…

    2026年5月10日
    000
  • c++中的SFINAE技术是什么_c++模板编程中的SFINAE原理与应用

    SFINAE 是“替换失败不是错误”的原则,指模板实例化时若参数替换导致错误,只要存在其他合法候选,编译器不报错而是继续重载决议。它用于条件启用模板、类型检测等场景,如通过 decltype 或 enable_if 控制函数重载,实现类型特征判断。尽管 C++20 引入 Concepts 简化了部分…

    2026年5月10日
    000
  • Go语言mgo查询构建:深入理解bson.M与日期范围查询的正确实践

    本文旨在解决go语言mgo库中构建复杂查询时,特别是涉及嵌套`bson.m`和日期范围筛选的常见错误。我们将深入剖析`bson.m`的类型特性,解释为何直接索引`interface{}`会导致“invalid operation”错误,并提供一种推荐的、结构清晰的代码重构方案,以确保查询条件能够正确…

    2026年5月10日
    100
  • vscode上怎么运行html_vscode上运行html步骤【指南】

    首先保存文件为.html格式,再通过浏览器或Live Server插件打开预览;推荐安装Live Server实现本地服务器运行与实时刷新,提升开发体验。 在 VS Code 上运行 HTML 文件并不需要复杂的配置,只需几个简单步骤即可预览页面效果。VS Code 本身是一个代码编辑器,不直接运行…

    2026年5月10日
    100
  • 修复点击时按钮抖动:CSS垂直对齐实践

    本文探讨了在Web开发中,交互式按钮(如播放/暂停按钮)在点击时发生意外垂直位移的问题。通过分析CSS样式变化对元素布局的影响,我们发现这是由于按钮不同状态下的边框样式和内边距改变,以及默认的垂直对齐行为共同作用所致。核心解决方案是利用CSS的vertical-align属性,将其设置为middle…

    2026年5月10日
    100
  • Golang goroutine与channel调试技巧

    使用go run -race检测数据竞争,结合runtime.NumGoroutine监控协程数量,通过pprof分析阻塞调用栈,利用select超时避免永久阻塞,有效排查goroutine泄漏、死锁和数据竞争问题。 Go语言的goroutine和channel是并发编程的核心,但它们也带来了调试上…

    2026年5月10日
    000
  • 使用 Jupyter Notebook 进行探索性数据分析

    Jupyter Notebook通过单元格实现代码与Markdown结合,支持数据导入(pandas)、清洗(fillna)、探索(matplotlib/seaborn可视化)、统计分析(describe/corr)和特征工程,便于记录与分享分析过程。 Jupyter Notebook 是进行探索性…

    2026年5月10日
    000
  • 《魔兽世界》将于6月11日开启国服回归技术测试

    《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试

    《%ign%ignore_a_1%re_a_1%》官方宣布,将于6月11日开启国服回归技术测试,时间为7天,并称可以在6月内正式开服,玩家们可以访问官网下载战网客户端并预下载“巫妖王之怒”客户端,技术测试详情见下图。 WordAi WordAI是一个AI驱动的内容重写平台 53 查看详情 以上就是《…

    2026年5月10日 用户投稿
    200
  • 如何在HTML中插入表单元素_HTML表单控件与输入类型使用指南

    HTML表单通过标签构建,包含action和method属性定义数据提交目标与方式,常用input类型如text、password、email等适配不同输入需求,配合label、required、placeholder提升可用性,结合textarea、select、button等控件实现完整交互,是…

    2026年5月10日
    100
  • 前端缓存策略与JavaScript存储管理

    根据数据特性选择合适的存储方式并制定清晰的读写与清理逻辑,能显著提升前端性能;合理运用Cookie、localStorage、sessionStorage、IndexedDB及Cache API,结合缓存策略与定期清理机制,可在保证用户体验的同时避免安全与性能隐患。 前端缓存和JavaScript存…

    2026年5月10日
    200
  • HTML5网页如何实现手势操作 HTML5网页移动端交互的处理技巧

    首先利用原生touch事件实现滑动判断,再通过preventDefault解决滚动冲突,接着引入Hammer.js处理复杂手势,最后通过优化点击区域、避免事件冲突和增加视觉反馈提升体验。 在移动端浏览器中,HTML5网页可以通过触摸事件实现手势操作,提升用户体验。虽然原生JavaScript提供了基…

    2026年5月10日
    000
  • 创建指定大小并填充特定数据的Golang文件教程

    本文将介绍如何使用Golang创建一个指定大小的文件,并用特定数据填充它。我们将使用 `os` 包提供的函数来创建和截断文件,从而实现快速生成大文件的目的。示例代码展示了如何创建一个10MB的文件,并将其填充为全零数据。掌握这些方法,可以方便地在例如日志系统或磁盘队列等场景中,预先创建测试文件或初始…

    2026年5月10日
    000
  • Python命令怎样使用profile分析脚本性能 Python命令性能分析的基础教程

    使用Python的cProfile模块分析脚本性能最直接的方式是通过命令行执行python -m cProfile your_script.py,它会输出每个函数的调用次数、总耗时、累积耗时等关键指标,帮助定位性能瓶颈;为进一步分析,可将结果保存为文件python -m cProfile -o ou…

    2026年5月10日
    000
  • 如何插入查询结果数据_SQL插入Select查询结果方法

    如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法

    使用INSERT INTO…SELECT语句可高效插入数据,通过NOT EXISTS、LEFT JOIN、MERGE语句或唯一约束避免重复;表结构不一致时可通过别名、类型转换、默认值或计算字段处理;结合存储过程可提升可维护性,支持参数化与动态SQL。 将查询结果数据插入到另一个表中,可以…

    2026年5月10日 用户投稿
    000
  • 使用 WebCodecs VideoDecoder 实现精确逐帧回退

    本文档旨在解决在使用 WebCodecs VideoDecoder 进行视频解码时,实现精确逐帧回退的问题。通过比较帧的时间戳与目标帧的时间戳,可以避免渲染中间帧,从而提高用户体验。本文将提供详细的解决方案和示例代码,帮助开发者实现精确的视频帧控制。 在使用 WebCodecs VideoDecod…

    2026年5月10日
    000
  • Discord.py 交互按钮超时与持久化解决方案

    本教程旨在解决Discord.py中交互按钮在一段时间后出现“This Interaction Failed”错误的问题。我们将深入探讨视图(View)的超时机制,并提供通过正确设置timeout参数以及利用bot.add_view()方法实现按钮持久化的具体方案,确保您的机器人交互功能稳定可靠,即…

    2026年5月10日
    000
  • Debian Copilot的社区活跃度如何

    debian copilot是codeberg社区维护的ai助手,旨在为debian用户提供服务。尽管搜索结果中没有直接提供关于debian copilot社区支持活跃度的具体数据,但我们可以通过debian社区的整体活跃度和特点来推断其活跃性。 Debian社区的一般情况: Debian拥有详尽的…

    2026年5月10日
    000
  • JavaScript 闭包:理解闭包原理与内存泄漏问题

    闭包是函数访问其外部作用域变量的能力,即使外部函数已执行完毕。如 inner 函数引用 outer 中的 count,形成闭包,使变量持久存在。闭包本身无害,但可能因延长变量生命周期导致内存泄漏,例如事件监听器引用大对象时。若未及时清理 DOM 事件或定时器,闭包会阻止垃圾回收,造成内存占用过高。解…

    2026年5月10日
    100

发表回复

登录后才能评论
关注微信