为了账号安全,请及时绑定邮箱和手机立即绑定

如何从流计算地图,然后检查地图值的属性?

如何从流计算地图,然后检查地图值的属性?

千巷猫影 2021-09-29 10:40:19
我的要求:我有一个只包含诸如public final static short SOME_CONST = whatever. 问题:短常量必须是unique。当有重复时,我主要对 SOME_CONST_A、SOME_CONST_B、...引起冲突的名称感兴趣。我编写了以下测试来通过反射进行测试。它有效,但我觉得它笨重而且不是很优雅:@Testpublic void testIdsAreUnique() {    Map<Short, List<String>> fieldNamesById = new LinkedHashMap<>();    Arrays.stream(InterfaceWithIds.class.getDeclaredFields())            .filter(f -> f.getClass().equals(Short.class))            .forEach((f) -> {        Short key = null;        String name = null;        try {            key = f.getShort(null);            name = f.getName();        } catch (IllegalAccessException e) {            throw new RuntimeException(e);        }        fieldNamesById.computeIfAbsent(key, x -> new ArrayList<>()).add(name);    });    assertThat(fieldNamesById.entrySet().stream().filter(e -> e.getValue().size() > 1)            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)), is(Collections.emptyMap()));}有没有办法避免中间的本地地图实例?
查看完整描述

3 回答

?
函数式编程

TA贡献1807条经验 获得超9个赞

这是一个按静态值对字段进行分组的流。请注意有关其他更改/更正的一些评论


Map<Short, List<String>> fieldNamesById = 

        Arrays.stream(InterfaceWithIds.class.getDeclaredFields())


         //using short.class, not Short.class

        .filter(f -> f.getType().equals(short.class)) 


        //group by value, mapping fields to their names in a list

        .collect(Collectors.groupingBy(f -> getValue(f),

                Collectors.mapping(Field::getName, Collectors.toList())));

调用读取值的方法如下(主要是为了避免流中的 try/catch 块):


private static Short getValue(Field f) {

    try {

        return f.getShort(null);

    } catch (Exception e) {

        throw new RuntimeException(e);

    }

}


查看完整回答
反对 回复 2021-09-29
?
大话西游666

TA贡献1817条经验 获得超14个赞

如果您希望有效地进行此检查(通常不那么关心单元测试),您可以通过乐观地假设字段没有重复并首先执行廉价的预测试来减少工作。此外,您可以使用此预测试的结果来获取不带Map.


作为先决条件,我们应该封装反射操作


private static int fieldValue(Field f) {

    try {

        return f.getShort(null);

    }

    catch(ReflectiveOperationException ex) {

        throw new IllegalStateException();

    }

}

此外,我们需要将short值范围的潜在值映射到 a 的正索引BitSet:


private static int shortToIndex(int shortValue) {

    return Math.abs(shortValue<<1) | (shortValue>>>31);

}

这假设具有较小量级的数字更常见并保持它们的量级较小,以减小结果BitSet. 如果假定这些值是正的,shortValue & 0xffff那就更可取了。如果两者都不适用,您也可以shortValue - Short.MIN_VALUE改用。


有了映射函数,我们可以使用


@Test

public void testIdsAreUnique() {

    BitSet value = new BitSet(), duplicate = new BitSet();


    Field[] fields = InterfaceWithIds.class.getDeclaredFields();

    Arrays.stream(fields)

        .filter(f -> f.getType() == short.class)

        .mapToInt(f -> shortToIndex(fieldValue(f)))

        .forEach(ix -> (value.get(ix)? duplicate: value).set(ix));


    if(duplicate.isEmpty()) return; // no duplicates


    throw new AssertionError(Arrays.stream(fields)

        .filter(f -> duplicate.get(shortToIndex(fieldValue(f))))

        .map(f -> f.getName()+"="+fieldValue(f))

        .collect(Collectors.joining(", ", "fields with duplicate values: ", "")));

}

它首先为所有遇到的值填充一个位集,并为多次遇到的值填充另一个位集。如果后一个位集为空,我们可以立即返回,因为没有重复。否则,我们可以使用该位集作为廉价过滤器来获取具有问题值的字段。


查看完整回答
反对 回复 2021-09-29
  • 3 回答
  • 0 关注
  • 118 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信