关于操作引用类型的对象的一些体会
bg:今天在做一个面试题的接口的时候,遇到一个多级目录的问题。其实也比较好理解实现思路。但丢给gpt给了一段代码让我有点钻牛角尖了
下面先给一下实体类对应的结构和sql吧
CREATE TABLE `xchl_category` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`category_pid` bigint(20) NOT NULL DEFAULT '0' COMMENT '顶级类目id',
`category_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '类目ID',
`category_name` varchar(30) NOT NULL DEFAULT '' COMMENT '类目名称',
`chinese_name` varchar(30) NOT NULL DEFAULT '' COMMENT '类目中文名',
`create_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `xchl_category_category_id_source_uindex` (`category_id`,`category_pid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1304 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='类目表';
import java.io.Serializable;
import java.util.List;
/**
* @author Ramsey
* @since 2024/6/9 12:49
*/
@Data
public class Category implements Serializable {
private Long id;
private Long categoryPid;
private Long categoryId;
private String categoryName;
private String chineseName;
private Long createTime;
private Long updateTime;
// 子类目列表
private List<Category> children;
}
上面是对应的实体类以及数据库字段,需求本质上就是要返回多级目录给到前端,下面是代码实现
List<Category> categories = categoryMapper.getAllCategories();
Map<Long, Category> categoryMap = new HashMap<>();
List<Category> topLevelCategories = new ArrayList<>();
// 将分类放入map,并找出顶级分类
for (Category category : categories) {
categoryMap.put(category.getCategoryId(), category);
if (category.getCategoryPid().equals(category.getCategoryId())) {
topLevelCategories.add(category);
}
}
// 递归建立分类树
for (Category category : categories) {
if (!category.getCategoryPid().equals(category.getCategoryId())) {
Category parent = categoryMap.get(category.getCategoryPid());
if (parent != null) {
if (parent.getChildren() == null) {
parent.setChildren(new ArrayList<>());
}
parent.getChildren().add(category);
}
}
}
return topLevelCategories;
这里其实就遇到我不理解的点了:为什么返回的是topLevelCategories但是第二次遍历的时候并没有对topLevelCategories进行操作,里面的内容却自动的讲父子关系填充好了?
询问gpt回复是:
通过这些操作,所有的分类对象被正确地组织在一起。如果某个分类是顶级分类,它的子分类会通过上述逻辑添加到它的
children
列表中。因为topLevelCategories
列表中的顶级分类是引用类型对象,它们的子分类会自动被填充。所以,尽管在第二次遍历中我们没有直接对topLevelCategories
列表进行操作,但topLevelCategories
中的每个顶级分类对象因为是引用类型,它们的内部结构已经被递归地填充完毕。因此,返回topLevelCategories
列表就已经包含了完整的分类树结构。
通过debug我们可以看到。在第二次遍历的时候。操作的是内存编号6899的放入了6900的父品牌里面。
那么操作结果也是显而易见的,实际上,我们并没有对顶级目录topLevelCategories进行操作,而是拿到了它里面的Category的引用,进行的父子目录的组合。从何实现了效果。这下也让我明白了对象引用的精髓
下面是gpt给的几个对象引用的案例,供大家学习参考
例子1:修改列表中的对象
假设我们有一个员工列表,每个员工有一个部门属性。如果我们想要修改从属于某一部门的所有员工的部门名称,我们可以直接操作对象,而不用直接修改列表。
class Department {
String name;
// constructor and getter/setter
public Department(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Employee {
String name;
Department department;
// constructor and getter/setter
public Employee(String name, Department department) {
this.name = name;
this.department = department;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
public class Main {
public static void main(String[] args) {
Department dept1 = new Department("Accounting");
Department dept2 = new Department("IT");
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("Alice", dept1));
employees.add(new Employee("Bob", dept2));
employees.add(new Employee("Charlie", dept1));
// 修改部门名称
dept1.setName("Finance");
// 打印员工信息
for (Employee emp : employees) {
System.out.println(emp.name + " works in " + emp.getDepartment().getName());
}
}
}
输出:
Alice works in Finance
Bob works in IT
Charlie works in Finance
在这个例子中,通过修改 dept1
的名称,所有引用 dept1
的员工对象的部门名称都被修改了。
例子2:树形结构的生成
假设我们有一个简单的树结构,我们可以通过操作子节点对象来构建整个树。
class TreeNode {
String value;
List<TreeNode> children;
public TreeNode(String value) {
this.value = value;
this.children = new ArrayList<>();
}
public void addChild(TreeNode child) {
this.children.add(child);
}
public List<TreeNode> getChildren() {
return children;
}
public String getValue() {
return value;
}
}
public class TreeDemo {
public static void main(String[] args) {
TreeNode root = new TreeNode("Root");
TreeNode child1 = new TreeNode("Child 1");
TreeNode child2 = new TreeNode("Child 2");
root.addChild(child1);
root.addChild(child2);
TreeNode grandchild1 = new TreeNode("Grandchild 1");
child1.addChild(grandchild1);
// 输出整棵树的结构
printTree(root, "");
}
public static void printTree(TreeNode node, String indent) {
System.out.println(indent + node.getValue());
for (TreeNode child : node.getChildren()) {
printTree(child, indent + " ");
}
}
}
输出:
Root
Child 1
Grandchild 1
Child 2
在这个例子中,通过操作节点对象的子节点引用,我们构建了一棵树,然后通过递归的方式将其打印出来。
例子3:共享对象的状态变更
假设我们有多个引用指向同一个对象,我们可以通过其中任何一个引用来改变这个对象的状态,其他引用将会反映出这些改变。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/b0a21bdb767742eab7713ee16964b916.png#pic_center)
class SharedObject {
String state;
public SharedObject(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
public class SharedObjectDemo {
public static void main(String[] args) {
SharedObject shared = new SharedObject("Initial State");
SharedObject ref1 = shared;
SharedObject ref2 = shared;
System.out.println("Ref1 state: " + ref1.getState());
System.out.println("Ref2 state: " + ref2.getState());
// 修改 shared 对象的状态
ref1.setState("Modified State");
// 其他引用也会反映出这些修改
System.out.println("Ref1 state: " + ref1.getState());
System.out.println("Ref2 state: " + ref2.getState());
}
}
输出:
Ref1 state: Initial State
Ref2 state: Initial State
Ref1 state: Modified State
Ref2 state: Modified State
在这个例子中,通过 ref1
修改 shared
对象的状态,ref2
也反映出了这些变化,因为 ref1
和 ref2
都引用了同一个 shared
对象。
总结
state: " + ref2.getState());
}
}
输出:
Ref1 state: Initial State
Ref2 state: Initial State
Ref1 state: Modified State
Ref2 state: Modified State
在这个例子中,通过 `ref1` 修改 `shared` 对象的状态,`ref2` 也反映出了这些变化,因为 `ref1` 和 `ref2` 都引用了同一个 `shared` 对象。
### 总结
这些例子证明了如何通过一个引用来修改对象,而其他指向同一对象的引用将反映这些修改。这也是为什么在多级分类树的构建中,通过 `categoryMap` 修改对象,最终返回的 `topLevelCategories` 会反映出所有的子分类结构。这是因为 `topLevelCategories` 中的对象引用已经被正确地构建和修改。