首先来看一段代码
Map<Integer, JSONObject> nodeMap = jsonArray.stream().map(obj -> (JSONObject) obj).peek(json -> json.put("children", new ArrayList<JSONObject>())).collect(Collectors.toMap(json -> json.getInteger("id"), json -> json));
这段代码的作用是将一个 JSONArray
对象转换成一个 Map<Integer, JSONObject>
, 并为每个对象预先创建了一个 children
的空列表。
其中键是每个 JSONObject
的 "id"
属性值,值是相应的 JSONObject
对象,并为每个对象添加一个空的 children
列表。它通过 Java Stream API 实现了这一转换,具体过程如下:
-
stream()
:将jsonArray
转换为流(Stream
),以便应用后续的操作。 -
map(obj -> (JSONObject) obj)
:将流中的每个对象(默认为Object
类型)强制转换为JSONObject
类型。 -
peek(json -> json.put("children", new ArrayList<JSONObject>()))
:对流中的每个JSONObject
对象执行副作用操作,向其中添加一个"children"
字段,该字段是一个空的ArrayList<JSONObject>
。 -
collect(Collectors.toMap(...))
:将流中的每个JSONObject
转换为Map
,其中键是JSONObject
中的"id"
字段,值是该JSONObject
本身。 -
json.getInteger("id")
假定了JSONObject
中的id
字段是Integer
类型,如果这个字段不存在或不是整数类型,可能会抛出异常。 -
peek
用于在不终止流的情况下对流中的元素执行额外操作,比如这里添加"children"
。
看了上面代码可能会有个疑问? 为什么json -> json.put(“children”, new ArrayList())要写在peek()中而不是写在map()中
json -> json.put("children", new ArrayList<JSONObject>())
写在 peek()
中而不是 map()
中的原因主要与这两个方法在 Java Stream API 中的用途和行为有关。
peek()
和 map()
的区别:
-
peek()
:- 用于在不干扰或修改流中元素的情况下,对每个元素执行一个动作(副作用)。
- 典型用法是用来调试或执行某些操作,比如日志记录、打印或修改某个对象的状态。
- 它返回的是原始流,不会对流中的元素进行变换。
-
map()
:- 用于将流中的每个元素转换为另一个对象或值。
- 其主要功能是对流中的每个元素应用一个函数,并返回一个新的流,流中的每个元素都会被映射到新的值。
为什么在 peek()
而不是 map()
中?
json.put("children", new ArrayList<JSONObject>())
只是对JSONObject
中的内容做了修改,但并没有生成一个新的对象。- 如果使用
map()
,则通常是为了将流中的每个元素转换为新的对象或者新的类型,这里并不需要将JSONObject
转换成其他东西,而只是修改了它的状态(添加了一个children
字段)。
具体场景分析:
.peek(json -> json.put("children", new ArrayList<JSONObject>()))
peek()
的作用是给每个JSONObject
添加children
字段,但不会改变JSONObject
的引用或类型。- 因为你并不需要修改流中元素的结构或类型,只是想附加某个动作,
peek()
是更合适的选择。
如果将此操作放在 map()
中,则会违反 map()
的设计目的(转换元素)。而使用 peek()
可以达到你的目的,即在不改变流内容的情况下进行操作。
总结:
peek()
是用于执行副作用而不改变流元素的。map()
是用于对流中的元素进行变换并返回一个新的流。
在此场景中,peek()
更合适,因为你只是对元素做了修改,而不是将其转换成一个新的对象。