有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

使用流API的Java中n字段分组算法

需要根据N个属性字段对List<Object>进行分组,这些字段在运行时决定。我怎样才能做到这一点

Group by multiple field names in java 8 提到这个问题,它使用的是固定数量的字段

例如:

Person{ int age, String city, Date doj, double salary}
record1: 25, NYC, 02/25/2018, 50000
record2: 25, MEX, 02/25/2017, 70000
record3: 26, MEX, 02/25/2017, 80000

GroupBy(city, doj)

Record1: = MEX, 02/25/2017, 150000
Record2: = NYC, 02/25/2018, 50000

工资将会增加

我将结果存储在Map<Object, List<Object>> 我已经完成了大部分。我面临的唯一问题是如何在groupingBy中更改键

Collectors.groupingBy( date ):第二次迭代将混乱所有城市的数据,这些城市将被city+date分组。如果我能将密钥更改为^{,这个问题就会解决 如何在第二次迭代中更改密钥Collectors.groupingBy( date )


共 (2) 个答案

  1. # 1 楼答案

    使用JB Nizet建议的解决方案,我已经整理出了一个完整的工作解决方案,您可以在其中按n个字段进行分组

    • 可以对任意数量的字段进行分组
    • 结果与分组字段顺序无关
    • 用户可以定义聚合策略

    这个嵌套属性将帮助我们存储分组的密钥

    public class NestedProperty {
        private final Field property;
        private final Object value;
    }
    

    这里的字段是一个简单的对象,将在运行时提供。我们可以有更好的选择来决定它的类型

    public class Field{
            String name;
            Class type;
        }
    

    这个接口应该由POGO实现,以定义什么是聚合策略

    public interface Aggregatable<T> {
            public void add(T o);
        }
    

    然后使用NestedProperty对象,我们使用流将记录分组到n-1个字段。groupby函数

    Map<List<NestedProperty>, List<T>> aggregatedRecords = objects.stream()
                        .collect(Collectors.groupingBy(r -> createGroupingKey(Nminus1fields, r), Collectors.toList()));
    
    
    private static List<NestedProperty> createGroupingKey(java.util.List<Field> fields, Object r) {
                return fields.stream().map(p -> p.toValue(r, p)).collect(Collectors.toList());
            }
    

    然后我们可以运行主聚合方法

    List<?> result = objects.stream().filter( r -> r!=null )
                    .collect(Collectors.groupingBy(
                            record -> {
                                try {
                                    return cast.cast(PropertyUtils.getNestedProperty(record, field.getName()));
                                }catch(Exception e) {
                                    System.out.println("Property not found.");
                                }
                                return null;
                            }))
                    .entrySet().stream()
                    .map( e -> e.getValue().stream()
                            .reduce((f1, f2) -> {
                                try {
                                    return (T) add(classDefination, f1, f2);
                                } catch (Exception e1) {
                                    System.out.println("Error is method add()");
                                }
                                return null;
                            })
                    ).map(f -> f.get())
                    .collect(Collectors.toList());
    

    请参考以下链接中的答案: http://www.unbounded.in/group-by-n-fields-in-java-like-sql-using-streams-api/

  2. # 2 楼答案

    下面是一个完整的例子:

    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.Objects;
    import java.util.stream.Collectors;
    
    public class Grouping {
    
        static final class Person {
            private final int age;
            private final String city;
            private final String doj;
            private final double salary;
    
            public Person(int age, String city, String doj, double salary) {
                this.age = age;
                this.city = city;
                this.doj = doj;
                this.salary = salary;
            }
    
            public int getAge() {
                return age;
            }
    
            public String getCity() {
                return city;
            }
    
            public String getDoj() {
                return doj;
            }
    
            public double getSalary() {
                return salary;
            }
    
            @Override
            public String toString() {
                return "Person{" +
                    "age=" + age +
                    ", city='" + city + '\'' +
                    ", doj='" + doj + '\'' +
                    ", salary=" + salary +
                    '}';
            }
        }
    
        enum Property {
            AGE {
                @Override
                protected Object extractValue(Person person) {
                    return person.getAge();
                }
            },
            CITY {
                @Override
                protected Object extractValue(Person person) {
                    return person.getCity();
                }
            },
            DOJ {
                @Override
                protected Object extractValue(Person person) {
                    return person.getDoj();
                }
            };
    
            protected abstract Object extractValue(Person person);
    
            public PropertyValue toValue(Person person) {
                return new PropertyValue(this, extractValue(person));
            }
        }
    
        static final class PropertyValue {
            private final Property property;
            private final Object value;
    
            public PropertyValue(Property property, Object value) {
                this.property = property;
                this.value = value;
            }
    
            @Override
            public boolean equals(Object o) {
                if (this == o) {
                    return true;
                }
                if (o == null || getClass() != o.getClass()) {
                    return false;
                }
                PropertyValue that = (PropertyValue) o;
                return property == that.property &&
                    Objects.equals(value, that.value);
            }
    
            @Override
            public int hashCode() {
                return Objects.hash(property, value);
            }
    
            @Override
            public String toString() {
                return "PropertyValue{" +
                    "property=" + property +
                    ", value=" + value +
                    '}';
            }
        }
    
        private static List<PropertyValue> createGroupingKey(List<Property> properties, Person person) {
            return properties.stream().map(property -> property.toValue(person)).collect(Collectors.toList());
        }
    
        public static void main(String[] args) {
            List<Person> persons = Arrays.asList(
                new Person(25, "NYC", "02/25/2018", 50000),
                new Person(25, "MEX", "02/25/2017", 70000),
                new Person(26, "MEX", "02/25/2017", 80000)
            );
    
            // TODO ask the user, rather than hardcoding
            List<Property> groupingProperties = Arrays.asList(Property.CITY, Property.DOJ);
    
            Map<List<PropertyValue>, Double> salaryAggregatedByChosenProperties =
                persons.stream()
                       .collect(Collectors.groupingBy(p -> createGroupingKey(groupingProperties, p),
                                                      Collectors.summingDouble(Person::getSalary)));
    
            System.out.println("salaryAggregatedByChosenProperties = " + salaryAggregatedByChosenProperties);
        }
    }
    

    它的作用是:

    1. 询问用户应该使用哪些属性进行分组(这实际上并没有完成,而是模拟的,因为这不是问题的核心)。返回一个List<Property>,其中包含(例如)属性CITY和DOJ
    2. 您将每个人转换为类型为List<PropertyValue>的分组密钥,因此,第一个人将转换为[NYC,2018年2月25日],而第二个人和第三个人都将转换为[MEX,2017年2月25日](因此具有相同的密钥)
    3. 你可以根据人的钥匙对他们进行分组
    4. 你把同一组人的工资加起来