比较TreeMap中的值

时间:2017-11-24 09:08:10

标签: java

我有一些日志存储在Map<Date, String>中,其中String表示一个Action,Date表示执行该操作的日期;

  Sat Jan 11 06:00:00 IST 2014=Read User
  Fri May 15 05:45:56 IST 2015=Update User
  Tue Nov 21 07:23:00 IST 2017=Create User  
  Tue Nov 21 08:34:00 IST 2017=Delete User
  Thu Nov 23 11:34:00 IST 2017=Create User
  Mon Jul 30 10:34:20 IST 2018=Delete User

我的要求是,对于日志,创建一个新地图,其中操作将是Old Action +具有相同值的Action,它们位于旧Map中。

示例输出:

Sat Jan 11 06:00:00 IST 2014=Read User
Fri May 15 05:45:56 IST 2015=Update User
Tue Nov 21 07:23:00 IST 2017=Create User  Create User [Thu Nov 23 11:34:00 
                                                                IST 2017 ]
Tue Nov 21 08:34:00 IST 2017=Delete User  Delete User [Mon Jul 30 10:34:20 
                                                                IST 2018 ]
Thu Nov 23 11:34:00 IST 2017=Create User
Mon Jul 30 10:34:20 IST 2018=Delete User

我的解决方案:

Map<Date, String> auditMap = new TreeMap<Date, String>();
Map<Date, String> auditMap2 = new TreeMap<Date, String>();

Collection<String> ls = auditMap.values();
Object[] arr = ls.toArray();
int count = 1;

for(Map.Entry<Date, String> d: auditMap.entrySet()) {               
    String da = d.getValue();

    for (int i = count ; i<arr.length ; i++) {
        if(d.getValue().equals(arr[i])) {
            da = da.concat("  ").concat((String) arr[i]).concat(" [" + auditMap.keySet().toArray()[i] +" ]");                       
        }
    }

    count++;
    auditMap2.put(d.getKey(), da);
}

for (Map.Entry<Date, String> d1: auditMap2.entrySet()) {
    System.out.println(d1);
}

当我使用两个for循环时,如何使代码更高效。无论如何我可以减少我的代码吗?

4 个答案:

答案 0 :(得分:1)

然后解决方案是将输入存储为包含List<Log_Objects>的{​​{1}}作为成员。然后使用groupingBy函数将Action & Data作为键并将Action作为Set值进行分组。见this

答案 1 :(得分:1)

以下算法是另一个版本,其O(n²)复杂度更具可读性:

Map<Date, String> auditMap = new TreeMap<>();
Map<Date, String> result = new TreeMap<>();

List<Map.Entry<Date, String>> entries = Lists.newArrayList(auditMap.entrySet());
for (int i = 0; i < entries.size(); i++) {
    Map.Entry<Date, String> entry = entries.get(i);

    // This SB will contain all next occurrences
    StringBuilder sb = new StringBuilder(entry.getValue());

    // Checks all the next occurrences in the iterator for equal values
    // To add them to the SB
    for (int j = i + 1; j < entries.size(); j++) {
        if (Objects.equals(entries.get(j).getValue(), entry.getValue())) {
            sb.append(" ").append(entry.getValue());
            sb.append(" [").append(entry.getKey()).append("]");
        }
    }

    // Builds the result map
    result.put(entry.getKey(), sb.toString());
}

复杂性分析:

原始地图上的循环计算n个项目。

嵌套循环然后计算(n - i + 1)项,依次为:(n - 1),(n - 2),...,1项。

总和是从1到n的总和,等于n *(n + 1)/ 2.这意味着总复杂度为O(n²)

答案 2 :(得分:1)

线性方法

可实现的最小复杂性为O(n),其中n是审计映射的大小,因为您需要至少遍历一次审计操作一次。

您的算法基本上是二次方(O(n^2))。

如果需要更好的复杂性,您需要确保迭代auditMap的每个元素:

Map<Date, String> auditMap = new TreeMap<Date, String>();
Map<String, AggregatedAction> aggregatedActions = new HashMap<>();

// Traverse each action once.
for (Map.Entry<Date, String> e : auditMap.entrySet()) {
  Date date = e.getKey();
  String action = e.getValue();

  // If the number of types of actions is small, the lookup in the 
  // aggregatedActions map can be estimated to be O(1).
  AggregatedAction aggregated = aggregatedActions.get(action);
  if (aggregated == null) {
    aggregated = new AggregatedAction(date, action);
    aggregatedActions.put(action, aggregated);

  } else {
    aggregated.append(date);
  }
}

// If you want a copy of your original map,
// otherwise you could just update it in place.
Map<Date, String> auditMap2 = new LinkedHashMap<Date, String>(auditMap);

// O(b) where b is the number of types of actions.
for (AggregatedAction action : aggregatedActions.values()) {
  auditMap2.put(action.firstOccurence, action.concatenatedAction.toString());
}

AggregatedAction定义为:

public static final class AggregatedAction {
  private final StringBuilder concatenatedAction = new StringBuilder();
  private final String action;
  private final Date firstOccurence;

  public AggregatedAction(Date firstOccurence, String action) {
    this.firstOccurence = firstOccurence;
    this.action = action;
    concatenatedAction.append(action);
  }

  public void append(Date nextOccurence) {
    concatenatedAction.append(" ").append(action).append(" [").append(nextOccurence).append(" ]");
  }
}

复杂性分析

  • n audit地图的尺寸​​
  • b aggregatedActions地图的尺寸​​

如果O(n + b) = O(n),则上面的代码会达到b << n

这里做出的一个重要假设是append操作的时间复杂度是不变的。事实并非如此,因为StringBuilder需要在内部字符串增长时重新分配数组。如果连接变得太昂贵,很难,我们可以考虑一个不同的构造,如列表或预先分配一个预先估计大小的数组,这将保证每个连接的恒定时间并使O(n)复杂性可能。但是我需要了解有关您的用例中涉及的大小的更多详细信息,以确定它是否值得。

此外,将输入地图复制到新的TreeMap可能会产生额外的O(nlog(n))费用。如果我们接受使用其他数据结构,例如列表或LinkedHashMap,我们就可以解决此问题。

答案 3 :(得分:0)

提高效果的步骤很少:

注意,我假设你想要到位解决方案。

1)根据值对TreeMap进行排序。它将帮助您停止迭代(您将理解的后续步骤)。 O(n logn)

2)创建一个临时变量,它将存储开始/最后处理的元素。

3)启动迭代并检查每个值。在这种情况下,如果外部循环值匹配,则内部循环将在那里提供下一个值并将其添加到先前的条目值。在每次迭代时,内部循环值将其从迭代器中删除,因此它将从树形图中删除,并将成为同一操作的值的一部分。

4)现在已经完成,你的地图将把所有的值作为输出。

注意,带有两个循环(外部和内部)的迭代器将花费O(n)次。所以你最坏的情况解决方案是O(n log n)。