动机
java的Stream在批量处理数据方面很强大。但是在面对类似json
等每部分都有不同语义的数据时就力不心了。正因为不像列表或集合,json
数据常常每部分有不同语义,所以要交由不同模块来处理。而最常用的map方法。只允许我们嵌入一个映射函数。当然,你可以在map函数里用if else 选择函数,但这就耦合了。(其实用groupingBy再entrySet().stream()
也不错,但是就不能惰性求值了)于是我写了个Function
实现类select
。根据select注入模块。模块只要做好自己的工作就好了,不用管数据从哪里来,从何而来,什么时候来。
本来想写个定位数据的功能,再把定位的数据交给处理模块,但发现太复杂了,其实只关注当前层和下一层会让方法简单。
select
package cn.lyf.demo;
import java.util.function.*;
import org.springframework.context.*;
import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*;
@Component
public class SelectBuilder {
@Autowired
ApplicationContext applicationContext;
public <T, R> Function<T, R> build(Function<? super T, String> dispatcher, Class<R> r) {
return new Select<>(dispatcher, r)::apply;
}
public <T,R> Function<T,?> build(Function<? super T, String> dispatcher) {
return new Select<>(dispatcher)::apply;
}
// 逻辑不等价
// 用select 列操作
public class Select<T, R> implements Function<T, R> {
private Function<T, String> dispatcher;
@Override
@SuppressWarnings("unchecked")
public R apply(T arg0) {
Function<T, ?> fx = (Function<T, ?>) applicationContext.getBean(dispatcher.apply(arg0));
return (R) fx.apply(arg0);
}
public Select(Function<T, String> dispatcher) {
this.dispatcher = dispatcher;
}
public Select(Function<T, String> dispatcher, Class<R> r) {
this.dispatcher = dispatcher;
}
}
}
另外一个问题就是stream似乎不支持把某个中间数据交给多个函数处理。类似神经网络那样。
实现类似下图矩阵的效果
(
a
b
c
)
×
(
f
g
h
)
→
(
f
∗
a
g
∗
a
h
∗
a
f
∗
b
g
∗
b
h
∗
b
f
∗
c
g
∗
c
h
∗
c
)
→
(
f
∗
a
+
g
∗
a
+
h
∗
a
f
∗
b
+
g
∗
b
+
h
∗
b
f
∗
c
+
g
∗
c
+
h
∗
c
)
\quad \begin{pmatrix} a \\ b \\ c \end{pmatrix} \quad \times \quad \begin{pmatrix} f & g & h \end{pmatrix} \quad \to \quad \begin{pmatrix} f*a & g*a & h*a \\ f*b & g*b & h*b \\ f*c & g*c & h*c \end{pmatrix} \quad \to \quad \begin{pmatrix} f*a + g*a + h*a \\ f*b + g*b + h*b \\ f*c + g*c + h*c \end{pmatrix} \quad
⎝⎛abc⎠⎞×(fgh)→⎝⎛f∗af∗bf∗cg∗ag∗bg∗ch∗ah∗bh∗c⎠⎞→⎝⎛f∗a+g∗a+h∗af∗b+g∗b+h∗bf∗c+g∗c+h∗c⎠⎞
这里和线性代数里的矩阵不一样,我自创的。
这里的*
和+
不是狭义的乘法加法。*
可以看作函数调用。+
可以看作把结果合并在一起的操作。
为什么最终要把每行加起来呢,其实是我是想为了每次运算不改变数据结构,如果输入是3X1
那么输出也是3X1
。从逻辑上也讲得通给一个数据获取一个结果。如果你不想把每行数据合在一起就用list::add
当作accumulator
就行了.
fork
package cn.lyf.demo;
import java.util.*;
import java.util.function.*;
import org.springframework.context.*;
import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*;
@Component
public class ForkBuilder {
@Autowired
ApplicationContext applicationContext;
public <T, R> Function<T, R> build(R identity, BinaryOperator<R> accumulator, Supplier<List<String>> getBeans) {
return new Fork<>(identity, accumulator, getBeans)::apply;
}
// 列表里是逻辑等价的
// 用fork 行操作
public class Fork<T, R> implements Function<T, R> {
BinaryOperator<R> accumulator;
Function<T, R>[] fxs;
@SuppressWarnings("unchecked")
public Fork(R identity, BinaryOperator<R> accumulator, Supplier<List<String>> getBeans) {
this.accumulator = accumulator;
fxs = getBeans.get().stream().map(s -> applicationContext.getBean(s)).toArray(Function[]::new);
}
@Override
public R apply(T t) {
List<R> result = new ArrayList<>();
for (Function<T, R> fx : fxs)
result.add(fx.apply(t));
return result.stream().reduce(accumulator).get();
}
}
}
二阶操作
初步设想是用fork
当行操作,select
当列操作,控制每列的fork
。这个行列的分类依据是同一行里的各个元素逻辑上是不同维度的(像身高,体重的关系)。同一列逻辑上是同一维度的(像小明的身高和小红身高的关系)。
(
a
b
c
d
)
×
(
f
g
h
w
)
→
(
f
∗
a
+
g
∗
a
h
∗
b
+
w
∗
b
f
∗
c
+
g
∗
c
h
∗
d
+
w
∗
d
)
\quad \begin{pmatrix} a & b \\ c & d \end{pmatrix} \quad \times \quad \begin{pmatrix} f & g \\ h & w\end{pmatrix} \quad \to \quad \begin{pmatrix} f*a+g*a & h*b+w*b \\f*c+g*c & h*d+w*d \end{pmatrix} \quad
(acbd)×(fhgw)→(f∗a+g∗af∗c+g∗ch∗b+w∗bh∗d+w∗d)