Java 对象列表-获取对象字段不同计数

Java 对象列表-获取对象字段不同计数,java,Java,对于对象列表,我必须检查(某些)字段: 该字段具有相同值的所有对象 该字段具有不同值的所有对象 班级人员{ 最后的字符串名; 最终积分; 最终int组; 公众人物(最终字符串名称、最终整数年龄、最终整数组){ this.name=名称; 这个。年龄=年龄; this.group=组; } 公共字符串getName(){ 返回此.name; } 公共整数getAge(){ 返回这个年龄; } public int getGroup(){ 返回此.group; } } 公共静态长距离字段(最终列

对于对象列表,我必须检查(某些)字段:

  • 该字段具有相同值的所有对象
  • 该字段具有不同值的所有对象
班级人员{
最后的字符串名;
最终积分;
最终int组;
公众人物(最终字符串名称、最终整数年龄、最终整数组){
this.name=名称;
这个。年龄=年龄;
this.group=组;
}
公共字符串getName(){
返回此.name;
}
公共整数getAge(){
返回这个年龄;
}
public int getGroup(){
返回此.group;
}
}
公共静态长距离字段(最终列表个人列表、最终函数字段){
return personList.stream()
.地图(现场)
.distinct().count();
}
公共静态void main(最终字符串[]args){
最终列表personList=Arrays.asList(
新人(“弗雷德”,25,1),
新人(“条例草案”,22,1),
新人(“弗雷德”,27,1),
新人(“丽莎”,25,1)
);
System.out.println(distinctByField(personList,Person::getName));
System.out.println(distinctByField(personList,Person::getAge));
System.out.println(distinctByField(personList,Person::getGroup));
}
根据stream/distinct/count的结果,我可以与当前列表大小进行比较:

  • 如果计数==1:该字段具有相同值的所有对象
  • if count==list.size:该字段具有不同值的所有对象
缺点是,我必须为每一个感兴趣的领域流


是否可以通过一个查询(针对感兴趣的字段列表)来完成此操作?

可以使用反射:

public class ReflectionTest {

    class Person {
        final String name;
        final int age;
        final int group;

        public Person(final String name, final int age, final int group) {
            this.name = name;
            this.age = age;
            this.group = group;
        }

        public String getName() {
            return this.name;
        }

        public int getAge() {
            return this.age;
        }

        public int getGroup() {
            return this.group;
        }
    }

    @DisplayName("should determine distinct fields")
    @Test
    public void distinct() {
        final List<Person> personList = Arrays.asList(new Person("Fred", 25, 1),
                                                      new Person("Bill", 22, 1),
                                                      new Person("Fred", 27, 1),
                                                      new Person("Lisa", 25, 1));
        Map<String,Long> fieldCountMap = Stream.of("name", "age", "group")
                                                  .map(fieldName -> ReflectionUtils.findField(Person.class, fieldName))
                                                  .filter(Objects::nonNull)
                                                  .collect(Collectors.toMap(Field::getName, field -> personList.stream().map(person -> getField(field, person)).distinct().count()));

        assertEquals(3,fieldCountMap.get("name"));
        assertEquals(1,fieldCountMap.get("group"));
        assertEquals(3,fieldCountMap.get("age"));
    }

    //extracted into a method because field.get throws a checked exception
    Object getField(Field field, Person object) {
        try {
            return field.get(object);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

}
公共类ReflectionTest{
班主任{
最后的字符串名;
最终积分;
最终int组;
公众人物(最终字符串名称、最终整数年龄、最终整数组){
this.name=名称;
这个。年龄=年龄;
this.group=组;
}
公共字符串getName(){
返回此.name;
}
公共整数getAge(){
返回这个年龄;
}
public int getGroup(){
返回此.group;
}
}
@DisplayName(“应确定不同的字段”)
@试验
公共空间{
最终列表personList=Arrays.asList(新的人(“Fred”),25,1),
新人(“条例草案”,22,1),
新人(“弗雷德”,27,1),
新人(“Lisa”,25,1));
Map fieldCountMap=Stream.of(“姓名”、“年龄”、“组别”)
.map(fieldName->ReflectionUtils.findField(Person.class,fieldName))
.filter(对象::非空)
.collect(Collectors.toMap(Field::getName,Field->personList.stream().map(person->getField(Field,person)).distinct().count());
assertEquals(3,fieldCountMap.get(“name”));
assertEquals(1,fieldCountMap.get(“组”);
assertEquals(3,fieldCountMap.get(“年龄”));
}
//已提取到方法中,因为field.get引发已检查的异常
对象getField(字段字段,人员对象){
试一试{
返回字段.get(对象);
}捕获(非法访问例外e){
抛出新的运行时异常(e);
}
}
}

首先让我提一下:这或多或少是代码质量的一个巨大缺点(手动搜索所有字段,使用额外的类存储结果)。我怀疑,这在计算时间或内存方面会更有效。根据逻辑,您必须触摸每个人的每个字段并存储已经发生的值,以便找到每个字段的不同计数。这正是使用3个流的解决方案所做的。我建议你坚持下去

但这里有一个解决方案。我构建了一个收集器,它在一次运行中将每个字段的所有不同值收集到一个自定义类中

  static class PersonStatistic {
    Set<String> names = new HashSet<>();
    Set<Integer> ages = new HashSet<>();
    Set<Integer> groups = new HashSet<>();
  }
  
  public static void main(final String[] args) {
    final List<Person> personList = Arrays.asList(
        new Person("Fred", 25, 1),
        new Person("Bill", 22, 1),
        new Person("Fred", 27, 1),
        new Person("Lisa", 25, 1));

    PersonStatistic personStatistic = personList.stream().collect(
        // Create new Statistic
        PersonStatistic::new, 
        // Merge A Person into statistic
        (statistic, person) -> {
          statistic.names.add(person.name);
          statistic.ages.add(person.age);
          statistic.groups.add(person.group);
        },
        // Merge second statistic into first
        (stat1, stat2)-> {
          stat1.names.addAll(stat2.names);
          stat1.ages.addAll(stat2.ages);
          stat1.groups.addAll(stat2.groups);
        });

    System.out.println(personStatistic.names.size());
    System.out.println(personStatistic.ages.size());
    System.out.println(personStatistic.groups.size());
  }
静态类PersonStatistic{
Set name=新的HashSet();
Set ages=新HashSet();
Set groups=newhashset();
}
公共静态void main(最终字符串[]args){
最终列表personList=Arrays.asList(
新人(“弗雷德”,25,1),
新人(“条例草案”,22,1),
新人(“弗雷德”,27,1),
新人(“Lisa”,25,1));
PersonStatistic PersonStatistic=personList.stream().collect(
//创建新的统计数据
新的,
//将某人合并到统计中
(统计数字,人)->{
统计.姓名.添加(个人.姓名);
统计.年龄.加(人.年龄);
统计.组.添加(个人.组);
},
//将第二个统计数据合并到第一个统计数据中
(stat1、stat2)->{
stat1.names.addAll(stat2.names);
stat1.ages.addAll(stat2.ages);
stat1.groups.addAll(stat2.groups);
});
System.out.println(personStatistic.names.size());
System.out.println(personStatistic.ages.size());
System.out.println(personStatistic.groups.size());
}

根据您的需要,您需要分别为每个字段运行。但是,您可以通过在字段上添加注释并运行一些反射来检测这些字段并在这些字段上运行检查,从而使其更加去极化。这将使其打开/关闭,但也会变得更复杂。您所说的一个查询(对于感兴趣的字段列表)可以实现这一点是什么意思?@YCF_L:我指的是一个更简单的语句,这样就不会对每个语句单独进行流式处理field@p.streef:如果有一个自己的收集器,它带有acculate/combine/finish,那该怎么办?当然,您可以从一个列表中编译一个报告,但这意味着您仍然可以在单独的字段上进行累积。基本上,在你要寻找的信息中