【问题标题】:List information aggregation列表信息聚合
【发布时间】:2012-03-20 10:13:28
【问题描述】:

我有以下场景:

class Task {
    int id;
    Group group;
    User user;
    boolean successful;
}

用户是组的一部分,组-用户关系是多对多的(用户可能属于多个组,一个组可能包含多个用户)。任务特定于组中的用户。

有一个List<Task>,我需要总结一个组中每个用户的成功任务数并向用户发送更新。因此,如果用户属于多个组,我需要为他所属的每个组更新一次(用户为该组完成的成功任务数)。

实现这一目标的最佳方法是什么?我们目前的算法是:

First, sort the list by Group ID and then by User ID.
Then: 

int successfulTasks = 0;
Group curGroup = null;
User curUser = null;
for(Task task : tasksByGroupAndUser) {
    if((task.getGroup() != curGroup) || (task.getUser() != curUser) {
        // Going to next user or group, update the previous user
        updateUser(user,group, successfulTasks);
        successfulTasks = 0;        
    }

    if(task.isSuccessful()) {
        successfulTasks++;
    }
}

// Handle last user  
if(curUser != null) {
    updateUser(user,group, successfulTasks); 
}

有没有更好(更清洁)的方法来做到这一点?以上似乎有点容易出错,尤其是最后一次用户检查。

【问题讨论】:

    标签: java algorithm list


    【解决方案1】:

    您可以创建一个Pair 类,该类将UserGroup 作为最终字段。确保Pair 覆盖hashCode()equals()

    创建一个HashMap<Pair,Integer>,并在迭代列表时将其维护[作为直方图]。 [只需要在名单上通过一次]。

    稍后,迭代直方图,并为每个 Pair [这实际上是一个元组:(User,Group)] 发送更新,其中包含成功运行的键。

    应该类似于:[伪代码,可能存在一些语法错误...]:

    Map<Pair,Integer> histogram = new HashMap<Pair,Integer>();
    for(Task task : tasksByGroupAndUser) {
       if (task.isSuccessful() == false) continue; //just skip unseccessful tasks.
       Pair current = new Pair(task.getUser(),task.getGroup());
       Integer value = histogram.get(current);
       histogram.put(current, value == null? 1 : value + 1); //update the histogram
    }
    for (Entry<Pair,Integer> entry : histogram.entrySet()) { 
       updateUser(entry.getKey().getUser(),entry.getKey().getGroup(),entry.getValue());
    }
    

    这个解决方案比建议的解决方案渐近更快,因为它不需要排序 - 所以总运行时间是 O(n)。 [而问题中建议的算法是O(nlogn)]。
    另外:我发现这个解决方案更容易理解,但这可能是一个见仁见智的问题......

    【讨论】:

    • 谢谢,我也朝着这个方向发展,但你的代码更好。速度在这里不是问题,但我也认为这个解决方案更干净,更容易理解。
    【解决方案2】:

    我会说用户对象应该有一个任务列表。 Task 对象应该只有 id、用途和 isSuccessfull。

    【讨论】:

    • 很遗憾我无法真正改变班级设计
    【解决方案3】:

    如果有人感兴趣,Guava 中有一个名为 Multiset 的类非常适合这种情况。它计算元素进入集合的次数。所以,根据阿米特的回答:

    Multiset<Pair> histogram = new HashMultiset<Pair>(); // Pair is a tuple of (User,Group)
    for(Task task : tasksByGroupAndUser) {
       if (task.isSuccessful()) {
          Pair current = new Pair(task.getUser(),task.getGroup());
          histogram.add(current);
       }
    }
    for (Pair pair : histogram) { 
       updateUser(pair.getUser(),pair.getGroup(),histogram.count(entry));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-10-23
      • 1970-01-01
      • 2016-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多