代码之家  ›  专栏  ›  技术社区  ›  GhostCat

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

  •  7
  • GhostCat  · 技术社区  · 7 年前

    public final static short SOME_CONST = whatever . 关键是:短常量需要 独特的 . 当有重复的时候,我主要感兴趣的是有一些常数A,一些常数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()));
    }
    

    (附加问题:有没有更好的方法来缩短用键/值对填充映射的lambda?)

    5 回复  |  直到 7 年前
        1
  •  4
  •   ernest_k Petronella    7 年前

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

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

    private static Short getValue(Field f) {
        try {
            return f.getShort(null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
        2
  •  4
  •   Holger    7 年前

    Map .

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

    private static int fieldValue(Field f) {
        try {
            return f.getShort(null);
        }
        catch(ReflectiveOperationException ex) {
            throw new IllegalStateException();
        }
    }
    

    此外,我们需要映射 short 值范围到 BitSet :

    private static int shortToIndex(int shortValue) {
        return Math.abs(shortValue<<1) | (shortValue>>>31);
    }
    

    这就假设了具有较小数量级的数字更为常见,并保持其数量级较小,以减小结果的大小 位集合 . 如果假设值为正, 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: ", "")));
    }
    

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

        3
  •  2
  •   Eugene    7 年前

     ...filter(..)
        .collect(Collectors.toMap(f -> f.getShort(null), Field::getName))
    

    如果存在重复项,则会出现异常而失败。抓住那个然后做 Assert.fail(...) 例如。

        4
  •  2
  •   davidxxx    7 年前

    以你的实际解决方案,你并不遥远。
    groupingBy() mapping() 在第一个映射中,按字段值收集字段名。 这样你就不需要任何中介了 Map .

    Map<Short, List<String>> map = 
    Arrays.stream(InterfaceWithIds.class.getDeclaredFields())
         .filter(f -> f.getType()
                       .getClass()
                       .equals(short.class))
         .map(f -> {
             Short key = null;
             String name = null;
             try {
                 key = f.getShort(null);
                 name = f.getName();
             } catch (IllegalAccessException e) {
                 throw new RuntimeException(e);
             }
             return new AbstractMap.SimpleEntry<>(key, name);
         })
         .collect(groupingBy(SimpleEntry::getKey, LinkedHashMap::new, mapping(e -> e.getValue(), Collectors.toList())))
         .entrySet()
         .stream()
         .filter(e -> e.getValue()
                       .size() > 1)
         .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    
    assertThat(map, is(Collections.emptyMap()));
    
        5
  •  1
  •   Michael    7 年前

    这里有一些问题。首先 f.getClass() Field 实例,而不是字段的实际类。你想要什么

    f.getType().equals(Short.class)
    

    接下来,你要记住这一点 Short.class short.class 是不同的,所以你真的想要

    f.getType().equals(Short.class) || f.getType().equals(short.class)
    

    我个人会利用这个事实 map.put assertNull 结果如何。

    整个测试如下所示:

    Map<Short, String> fieldNamesById = new LinkedHashMap<>();
    Arrays.stream(InterfaceWithIds.class.getDeclaredFields())
        .filter(f -> f.getType().equals(Short.class) || f.getType().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);
            }
    
            assertNull(fieldNamesById.put(key, name));
        });
    

    List<String> problems = new ArrayList<>();
    
    Map<Short, String> fieldNamesById = new LinkedHashMap<>();
    Arrays.stream(InterfaceWithIds.class.getDeclaredFields())
        .filter(f -> f.getType().equals(Short.class) || f.getType().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);
            }
    
            String prevValue = fieldNamesById.put(key, name);
            if (prevValue != null) problems.add("key " + key + " mapped to " + name + " and " + prevValue);
        });
    
    assertTrue(problems.toString(), problems.isEmpty());
    
    推荐文章