我有一些日志存储在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循环时,如何使代码更高效。无论如何我可以减少我的代码吗?
答案 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)。