Java 实现通用高效树结构转换算法2
在 Java 实现通用高效树结构转换算法 中介绍了一种转换树结构的算法。在微信公众号的回复中,404 N’F 指出 TreeNode 接口设计存在问题,经过沟通后发现确实有一种更适合直接使用的实现方式,本文就是基于这一新设计的实现。
TreeNewBee 新设计

图:左侧是新的实现,右侧为旧的实现
旧版本实现中定义了 TreeNode<T>
接口,因此想使用这个工具类,就需要先实现这个接口,会产生较强的耦合性,虽然算法简单,但使用起来不够便捷。怎么才能更方便呢?
答案就是 Lambda 和函数式接口。将 TreeNode<T>
里面的3个方法拆分成3个独立的函数式接口,这样就可以不直接实现接口而直接使用工具类,适配性更强。主要的缺点是针对同一对象或结构时代码复用性稍弱。
1 |
|
这3个接口都作为方法参数传递给构建树的方法,通过这3个函数式接口的组合来实现树结构的转换。
1 |
|
下面通过多个示例演示用法,并展示其更广泛的通用性。
示例
1. 方法引用
这是最常见的用法。如果转换树结构的对象本身就有类似上述3个接口的方法,就可以直接使用方法引用,示例如下:
1 |
|
这里直接使用方法引用,复用性不受影响。如果觉得 addChild
方法相比 getter、setter 不够常见,也可以去掉该方法,然后使用下面的方式:
1 |
|
如果你的 children 默认不初始化,还可以这样处理:
1 |
|
2. Map 类型
对于对象类型,有了getter、setter就基本离不开上面的几种情况。当使用 Map 时,就有了更加灵活的处理方式。下面是一个示例:
1 |
|
从上面示例可以看到函数式接口的灵活性和通用性。关于算法的内容就介绍到这里,接下来看一个你可能没有注意到的方法引用用法。
有趣的 Node::addChild
参数
不知道你有没有注意到,buildTree
最后一个参数是 AddChild<R, C>
接口,其方法签名是 void addChild(R parent, C child)
,有两个参数,无返回值。但是我们使用时传递的 Node::addChild
,这个方法的定义如下:
1 |
|
这个方法无返回值,只有一个参数,和我们接口的定义并不一致,为什么可以使用呢?
根据Java语言规范(JLS)15.13节,方法引用有四种形式:
- 静态方法引用:
ClassName::staticMethod
- 实例方法引用(绑定接收者):
instance::instanceMethod
- 实例方法引用(未绑定接收者):
ClassName::instanceMethod
- 构造方法引用:
ClassName::new
Node::addChild
属于第3种:未绑定接收者的实例方法引用。
编译时的类型推断过程,JLS规定,编译器会根据目标类型进行类型推断:
1 |
|
JLS 15.13.3规定了参数映射规则:
- 对于未绑定的实例方法引用
C::m
- 如果目标函数式接口有n个参数,则:
- 第1个参数作为方法调用的接收者
- 第2到第n个参数作为方法m的实际参数
通过函数式接口的重新设计,我们成功解决了旧版本中接口耦合性强的问题。新的 TreeNewBee 实现不仅保持了原有算法的高效性,还大大提升了使用的便捷性和适配性。无论是普通对象、Map 类型,还是其他自定义结构,都可以通过简单的 Lambda 表达式或方法引用轻松实现树结构转换。虽然在代码复用性上有所权衡,但这种设计更符合现代 Java 开发中”组合优于继承”的理念,真正做到了开箱即用。