以高效的方式处理稀疏数据

时间:2016-02-24 12:46:11

标签: java java-8

我想不出更好的标题。对此感到抱歉。如果有人能想到更好的标题,请随时改变它。

Rule {
    String key;
    String t1; // First level
    String t2;
    String t3; // Last level
    Object value;
}

RuleHolder {
    Collection<Rule> rules;
}

示例数据可以在RuleHolder中,如下所示(顺序可以不同)

key     t1   t2   t3   value 
 A      a    -    -     m
 A      a    b    -     mm
 A      a    b    c     mmm
 B      a    -    -     n
 C      a    -    -     p
 C      a    b    -     pp

期望的结果:

Key A   ->   mmm
Key B   ->   n
Key C   ->   pp

现在我怎样才能以高效的方式达到理想的效果?为了增加更多的复杂性,我如何获得单个密钥的最佳值t1,t2,t3如getBestValue(key,t1,t2,t3)可能对于t1,t2,t3的组合没有值。

  

我的尝试如下:

步骤1: 首先从收集规则准备一个Map;

Map<key, Collection<Rule>>

步骤2:在关键环境中,完成所有规则,然后选取最后一级定义的值。

1 个答案:

答案 0 :(得分:1)

使用Stream API,您确实需要有2个单独的Stream管道:

  • 第一个将创建一个Map<String, Rule>,其中一个键指向要保留的规则
  • 第二个结果会将第一个结果映射到新的Map<String, Object>,我们只保留规则的值

使用toMap收集器创建第一个映射,并通过选择具有最多非null级别元素的值来处理重复键。因此它首先检查t3并选择非空的规则;如果不存在,则保留t2非空的规则;因为那些是硬编码的变量,除了涉及反射之外我们不能使它真正动态。

最后,最后一张地图也是由toMap收藏家创建的,只需简单地重新映射该值。

Map<String, Rule> ruleMap =
    rules.stream().collect(Collectors.toMap(
        r -> r.key,
        r -> r,
        (r1, r2) -> { // a bit ugly but it comes from the fact that t1, t2 an t3 are hard-coded
            if (r1.t3 != null) return r1;
            if (r2.t3 != null) return r2;
            if (r1.t2 != null) return r1;
            if (r2.t2 != null) return r2;
            if (r1.t1 != null) return r1;
            if (r2.t1 != null) return r2;
            return r1;
        }
    ));

Map<String, Object> result = 
    ruleMap.entrySet()
           .stream()
           .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().value));

System.out.println(result); // prints "{A=mmm, B=n, C=pp}" for your example

为了让它变得动态,我们需要添加一些魔力。给定一个字段列表,我们可以将其映射到List<MethodHandle>

List<String> fields = Arrays.asList("t3", "t2", "t1");
MethodHandles.Lookup lookup = MethodHandles.lookup();
List<MethodHandle> handles = fields.stream().map(s -> {
    try {
        return lookup.findGetter(Rule.class, s, String.class);
    } catch (ReflectiveOperationException e) {
        throw new AssertionError(e);
    }
}).collect(Collectors.toList());

然后我们可以使用该列表在重复的情况下返回最合适的规则:

Map<String, Rule> ruleMap =
    rules.stream().collect(Collectors.toMap(
        r -> r.key,
        r -> r,
        (r1, r2) -> {
            return handles.stream().map(h -> {
                try {
                    if (h.invoke(r1) != null) return r1;
                    if (h.invoke(r2) != null) return r2;
                    return null;
                } catch (Throwable t) {
                    throw new AssertionError(t);
                }
            }).filter(Objects::nonNull).findFirst().orElse(r1);
        }
    ));