有 Java 编程相关的问题?

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

arraylist Java中“分组依据”和聚合值的最佳数据结构?

我创建了一个数组类型的ArrayList,如下所示

ArrayList<Object[]> csvArray = new ArrayList<Object[]>();

如您所见,ArrayList的每个元素都是类似于{Country,City,Name,Age}的数组

现在我想在国家城市上做一个“分组,然后计算每个国家+城市的平均年龄

请问最简单的方法是什么?或者你们有没有建议在这种“分组依据”和聚合需求中使用比ArrayList更好的数据结构

非常感谢你的回答


共 (6) 个答案

  1. # 1 楼答案

    您可以使用Java8流来实现这个和^{}。例如:

    final List<Object[]> data = new ArrayList<>();
    data.add(new Object[]{"NL", "Rotterdam", "Kees", 38});
    data.add(new Object[]{"NL", "Rotterdam", "Peter", 54});
    data.add(new Object[]{"NL", "Amsterdam", "Suzanne", 51});
    data.add(new Object[]{"NL", "Rotterdam", "Tom", 17});
    
    final Map<String, List<Object[]>> map = data.stream().collect(
            Collectors.groupingBy(row -> row[0].toString() + ":" + row[1].toString()));
    
    for (final Map.Entry<String, List<Object[]>> entry : map.entrySet()) {
        final double average = entry.getValue().stream()
                                    .mapToInt(row -> (int) row[3]).average().getAsDouble();
        System.out.println("Average age for " + entry.getKey() + " is " + average);
    }
    
  2. # 2 楼答案

    在java 8中,通过使用收集器,可以简化基于一个或多个属性的值对集合中的对象进行分组的想法

    首先,我建议您添加一个新类,如下所示

    class Info {
    
        private String country;
        private String city;
        private String name;
        private int age;
    
        public Info(String country,String city,String name,int age){
            this.country=country;
            this.city=city;
            this.name=name;
            this.age=age;
        }
    
        public String toString() {
             return "("+country+","+city+","+name+","+age+")";
        }
    
       // getters and setters       
    
    }
    

    设置infos

       ArrayList<Info> infos  =new  ArrayList();
    
    
       infos.add(new Info("USA", "Florida", "John", 26));
       infos.add(new Info("USA", "Florida", "James", 18));
       infos.add(new Info("USA", "California", "Alan", 30));
    

    按国家+城市分组:

      Map<String, Map<String, List<Info>>> 
               groupByCountryAndCity = infos.
                 stream().
                   collect(
                        Collectors.
                            groupingBy(
                                Info::getCountry,
                                Collectors.
                                    groupingBy(
                                         Info::getCity     
                                              )
                                       )
                         );
    
    
        System.out.println(groupByCountryAndCity.get("USA").get("California"));
    

    输出

    [(USA,California,James,18), (USA,California,Alan,30)]
    

    每个国家和城市的人口平均年龄:

        Map<String, Map<String, Double>> 
        averageAgeByCountryAndCity = infos.
             stream().
               collect(
                 Collectors.
                     groupingBy(
                        Info::getCountry,
                         Collectors.
                             groupingBy(
                                 Info::getCity,
                                 Collectors.averagingDouble(Info::getAge)
                                       )
                                )
                  );
    
         System.out.println(averageAgeByCountryAndCity.get("USA").get("Florida"));
    

    输出:

    22.0
    
  3. # 3 楼答案

    我建议采取额外的措施。从对象[]中的CSV收集数据。如果将数据封装到包含这些数据的类中,java8集合将很容易帮助您。(也没有,但更易读易懂)

    下面是一个例子——它引入了一个类Information,其中包含给定的数据(国家、城市、姓名、年龄)。该类有一个构造函数,通过给定的Object[]数组初始化这些字段,这可能有助于您这样做,但是:这些字段必须是固定的(通常用于CSV):

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    public class CSVExample {
    
      public static void main(String[] args) {
        ArrayList<Information> csvArray = new ArrayList<>();
    
        csvArray.add(new Information(new Object[] {"France", "Paris", "Pierre", 34}));
        csvArray.add(new Information(new Object[] {"France", "Paris", "Madeleine", 26}));
        csvArray.add(new Information(new Object[] {"France", "Toulouse", "Sam", 34}));
        csvArray.add(new Information(new Object[] {"Italy", "Rom", "Paul", 44}));
    
    // combining country and city with whitespace delimiter to use it as the map key
        Map<String, List<Information>> collect = csvArray.stream().collect(Collectors.groupingBy(s -> (s.getCountry() + " " + s.getCity())));
    //for each key (country and city) print the key and the average age
        collect.forEach((k, v) -> System.out.println(k + " " + v.stream().collect(Collectors.averagingInt(Information::getAge))));
      }
    }
    
    class Information {
      private String country;
      private String city;
      private String name;
      private int age;
    
      public Information(Object[] information) {
        this.country = (String) information[0];
        this.city = (String) information[1];
        this.name = (String) information[2];
        this.age = (Integer) information[3];
    
      }
    
      public Information(String country, String city, String name, int age) {
        super();
        this.country = country;
        this.city = city;
        this.name = name;
        this.age = age;
      }
    
      public String getCountry() {
        return country;
      }
    
      public String getCity() {
        return city;
      }
    
      public String getName() {
        return name;
      }
    
      public int getAge() {
        return age;
      }
    
      @Override
      public String toString() {
        return "Information [country=" + country + ", city=" + city + ", name=" + name + ", age=" + age + "]";
      }
    
    }
    

    main显示了问题的简单输出

  4. # 4 楼答案

    您将在Java 8中获得许多选项

    示例

     Stream<Person> people = Stream.of(new Person("Paul", 24), new Person("Mark",30), new Person("Will", 28));
     Map<Integer, List<String>> peopleByAge = people
    .collect(groupingBy(p -> p.age, mapping((Person p) -> p.name, toList())));
     System.out.println(peopleByAge);
    

    如果您可以使用Java8,并且没有使用数据结构的具体原因,那么可以阅读下面的教程

    http://java.dzone.com/articles/java-8-group-collections

  5. # 6 楼答案

    您可以查看@duffy356推荐的收藏。我可以给你一个与^{相关的标准解决方案

    我会使用一个公共的Map<Key,Value>和一个特定的HashMap
    正如我所看到的,对于钥匙,你需要一个和国家和城市有关的非常简单的物体。重点是创建一个有效的equals(Object) : boolean方法。我会使用Eclipse自动生成器;对我来说,它给了我以下信息:

    class CountryCityKey {
     // package visibility
     String country;
     String city;
    
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + ((country == null) ? 0 : country.hashCode());
      result = prime * result + ((region == null) ? 0 : region.hashCode());
      return result;
    }
    
    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      CountryCityKey other = (CountryCityKey) obj;
      if (country == null) {
        if (other.country != null)
          return false;
      } else if (!country.equals(other.country))
        return false;
      if (region == null) {
        if (other.region != null)
          return false;
      } else if (!region.equals(other.region))
        return false;
      return true;
    }
    

    }


    现在我们可以在HashMap<CountryCityKey, MySuperObject>中对一个或多个对象进行分组

    其代码可能是:

    Map<CountryCityKey, List<MySuperObject>> group(List<MySu0perObject> list) {
      Map<CountryCityKey, MySuperObject> response = new HashMap<>(list.size());  
      for (MySuperObject o : list) {
         CountryCityKey key = o.getKey(); // I consider this done, so simply
         List<MySuperObject> l;
         if (response.containsKey(key)) {
            l = response.get(key);
         } else {
            l = new ArrayList<MySuperObject>();
         }
         l.add(o);
         response.put(key, l);
      }
      return response;
    }
    

    你有:)