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

我的要求:我有一个只包含诸如public final static short SOME_CONST = whatever. 问题:短常量必须是unique。当有重复时,我主要对 SOME_CONST_A、SOME_CONST_B、...引起冲突的名称感兴趣。


我编写了以下测试来通过反射进行测试。它有效,但我觉得它笨重而且不是很优雅:


@Test

public 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()));

}

有没有办法避免中间的本地地图实例?


千巷猫影
浏览 119回答 3
3回答

函数式编程

这是一个按静态值对字段进行分组的流。请注意有关其他更改/更正的一些评论Map<Short, List<String>> fieldNamesById =&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; Arrays.stream(InterfaceWithIds.class.getDeclaredFields())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//using short.class, not Short.class&nbsp; &nbsp; &nbsp; &nbsp; .filter(f -> f.getType().equals(short.class))&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; //group by value, mapping fields to their names in a list&nbsp; &nbsp; &nbsp; &nbsp; .collect(Collectors.groupingBy(f -> getValue(f),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Collectors.mapping(Field::getName, Collectors.toList())));调用读取值的方法如下(主要是为了避免流中的 try/catch 块):private static Short getValue(Field f) {&nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; return f.getShort(null);&nbsp; &nbsp; } catch (Exception e) {&nbsp; &nbsp; &nbsp; &nbsp; throw new RuntimeException(e);&nbsp; &nbsp; }}

大话西游666

如果您希望有效地进行此检查(通常不那么关心单元测试),您可以通过乐观地假设字段没有重复并首先执行廉价的预测试来减少工作。此外,您可以使用此预测试的结果来获取不带Map.作为先决条件,我们应该封装反射操作private static int fieldValue(Field f) {&nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; return f.getShort(null);&nbsp; &nbsp; }&nbsp; &nbsp; catch(ReflectiveOperationException ex) {&nbsp; &nbsp; &nbsp; &nbsp; throw new IllegalStateException();&nbsp; &nbsp; }}此外,我们需要将short值范围的潜在值映射到 a 的正索引BitSet:private static int shortToIndex(int shortValue) {&nbsp; &nbsp; return Math.abs(shortValue<<1) | (shortValue>>>31);}这假设具有较小量级的数字更常见并保持它们的量级较小,以减小结果BitSet. 如果假定这些值是正的,shortValue & 0xffff那就更可取了。如果两者都不适用,您也可以shortValue - Short.MIN_VALUE改用。有了映射函数,我们可以使用@Testpublic void testIdsAreUnique() {&nbsp; &nbsp; BitSet value = new BitSet(), duplicate = new BitSet();&nbsp; &nbsp; Field[] fields = InterfaceWithIds.class.getDeclaredFields();&nbsp; &nbsp; Arrays.stream(fields)&nbsp; &nbsp; &nbsp; &nbsp; .filter(f -> f.getType() == short.class)&nbsp; &nbsp; &nbsp; &nbsp; .mapToInt(f -> shortToIndex(fieldValue(f)))&nbsp; &nbsp; &nbsp; &nbsp; .forEach(ix -> (value.get(ix)? duplicate: value).set(ix));&nbsp; &nbsp; if(duplicate.isEmpty()) return; // no duplicates&nbsp; &nbsp; throw new AssertionError(Arrays.stream(fields)&nbsp; &nbsp; &nbsp; &nbsp; .filter(f -> duplicate.get(shortToIndex(fieldValue(f))))&nbsp; &nbsp; &nbsp; &nbsp; .map(f -> f.getName()+"="+fieldValue(f))&nbsp; &nbsp; &nbsp; &nbsp; .collect(Collectors.joining(", ", "fields with duplicate values: ", "")));}它首先为所有遇到的值填充一个位集,并为多次遇到的值填充另一个位集。如果后一个位集为空,我们可以立即返回,因为没有重复。否则,我们可以使用该位集作为廉价过滤器来获取具有问题值的字段。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java