通过java中的两个字段对对象进行分组

时间:2011-03-30 21:37:11

标签: java

我有很多日志文件,我想在java中处理它们,但我想先对它们进行排序,以便我可以获得更多人类可读的结果。

我的日志类:

public class Log{
//only relevant fields here
private String countryCode;
private AccessType accessType;
...etc..
}

AccessType是Enum,其值为WEB,API,OTHER。

我想用countryCode和accessType对Log对象进行分组,这样最终产品就是日志列表。

我得到了这个工作,可以通过countryCode将日志分组到日志列表中,如下所示:

public List<Log> groupByCountryCode(String countryCode) {
        Map<String, List<Log>> map = new HashMap<String, List<Log>>();
        for (Log log : logList) {
            String key = log.getCountryCode();
            if (map.get(key) == null) {
                map.put(key, new ArrayList<Log>());
            }
            map.get(key).add(log);
        }
        List<Log> sortedByCountryCodeLogList = map.get(countryCode);

        return sortedByCountryCodeLogList;
    }

来自@Kaleb Brasee的例子:

Group by field name in Java

这是我现在已经尝试了一段时间了,现在真的卡住了..

public List<Log> groupByCountryCode(String countryCode) {
        Map<String, Map<AccessType, List<Log>>> map = new HashMap<String, Map<AccessType, List<Log>>>();
        AccessType mapKey = null;
        List<Log> innerList = null;
        Map<AccessType, List<Log>> innerMap = null;
        // inner sort
        for (Log log : logList) {
            String key = log.getCountryCode();
            if (map.get(key) == null) {
                map.put(key, new HashMap<AccessType, List<Log>>());
                innerMap = new HashMap<AccessType, List<Log>>();
            }

            AccessType innerMapKey = log.getAccessType();
            mapKey = innerMapKey;
            if (innerMap.get(innerMapKey) == null) {
                innerMap.put(innerMapKey, new ArrayList<Log>());
                innerList = new ArrayList<Log>();
            }

            innerList.add(log);
            innerMap.put(innerMapKey, innerList);
            map.put(key, innerMap);
            map.get(key).get(log.getAccessType()).add(log);
        }

        List<Log> sortedByCountryCodeLogList = map.get(countryCode).get(mapKey);

        return sortedByCountryCodeLogList;
    }

我不确定我知道自己在做什么

4 个答案:

答案 0 :(得分:4)

你的问题令人困惑。您想要对列表进行排序,但是您要创建许多新列表,然后丢弃除其中一个之外的所有列表?

这是一种对列表进行排序的方法。请注意Collections.sort()使用稳定排序。 (这意味着将保留一组国家/地区代码和访问类型中的项目的原始顺序。)

class MyComparator implements Comparator<Log> {
  public int compare(Log a, Log b) {
    if (a.getCountryCode().equals(b.getCountryCode()) {
      /* Country code is the same; compare by access type. */
      return a.getAccessType().ordinal() - b.getAccessType().ordinal();
    } else
      return a.getCountryCode().compareTo(b.getCountryCode());
  }
}
Collections.sort(logList, new MyComparator());

如果你真的想要做你的代码当前正在做的事情,至少跳过创建不必要的列表:

public List<Log> getCountryAndAccess(String cc, AccessType access) {
  List<Log> sublist = new ArrayList<Log>();
  for (Log log : logList) 
    if (cc.equals(log.getCountryCode()) && (log.getAccessType() == access))
      sublist.add(log);
  return sublist;
}

答案 1 :(得分:1)

如果您能够使用它,Google的Guava库中有一个Ordering类可能有助于简化操作。这样的事情可能有用:

   Ordering<Log> byCountryCode = new Ordering<Log>() {
     @Override
     public int compare(Log left, Log right) {
        return left.getCountryCode().compareTo(right.getCountryCode());
     }
   };

   Ordering<Log> byAccessType = new Ordering<Log>() {
     @Override
     public int compare(Log left, Log right) {
        return left.getAccessType().compareTo(right.getAccessType());
     }
   };
   Collections.sort(logList, byCountryCode.compound(byAccessType));

答案 2 :(得分:0)

您应首先创建新的内部地图,然后将其添加到外部地图:

        if (map.get(key) == null) {
            innerMap = new HashMap<AccessType, List<Log>>();
            map.put(key, innerMap);
        }

和list元素类似。这样可以避免创建不必要的地图元素,然后再将其覆盖。

总的来说,最简单的方法是使用与第一种方法相同的逻辑,即如果地图中没有元素,插入它,然后从地图中获取它:

    for (Log log : logList) {
        String key = log.getCountryCode();
        if (map.get(key) == null) {
            map.put(key, new HashMap<AccessType, List<Log>>());
        }

        innerMap = map.get(key);
        AccessType innerMapKey = log.getAccessType();
        if (innerMap.get(innerMapKey) == null) {
            innerMap.put(innerMapKey, new ArrayList<Log>());
        }
        innerMap.get(innerMapKey).add(log);
    }

答案 3 :(得分:0)

首先,看起来你要在for循环中将每个日志条目添加两次,最后一行map.get(key).get(log.getAccessType()).add(log);。鉴于上面的代码,我认为你可以不用它。

修好后,要返回List<Log>,您可以执行以下操作:

List<Log> sortedByCountryCodeLogList = new ArrayList<Log>();
for (List<Log> nextLogs : map.get(countryCode).values()) {
    sortedByCountryCodeLogList.addAll(nextLogs);
}

我认为上面的代码应该将其展平为一个列表,仍然按国家/地区代码和访问类型进行分组(不是按插入顺序排列,因为您使用的是HashMap而不是LinkedHashMap),我想想就是你想要的。