[英]Java Comparator Sorting Problem Using Map Values
我有一種情況,我需要使用Map<String, Set<String>>
,並將它們添加到已排序的新Set<String>
中。 排序順序基於每個鍵的Map值。 映射的每個鍵的值是一個Set,其中包含與該鍵相關的其他鍵。
我需要對鍵進行排序,以使相關鍵必須在包含其相關Set中的另一個鍵之前。 要使用編程范例,這類似於要求在更早的一行上聲明一個變量,然后才能在另一行上對其進行引用。
例如,以下內容表示Map<String, Set<String>>
:
abc=[def, ghi, jkl, mno]
def=[]
ghi=[def]
jkl=[ghi, stu]
mno=[]
pqr=[abc]
stu=[def]
vwx=[mno, ghi]
zy0=[jkl]
在此示例中,鍵“ jkl”與鍵具有關系,“ ghi”和“ stu”,“ def”與任何鍵都不具有關系。
注意:關系將是單向的。 因此,例如,如果“ ghi”與“ def”相關,則“ def”將永遠與“ ghi”相關。
因此,對於上述Map,排序順序為:
def=[]
mno=[]
ghi=[def]
stu=[def]
vwx=[mno, ghi]
jkl=[ghi, stu]
zy0=[jkl]
abc=[def, ghi, jkl, mno]
pqr=[abc]
這是我寫的比較器。 它在使用上述示例的可運行測試類中:
import java.util.*;
public class RelationshipComparator_Test {
public static void main(String[] args) {
String[] testMap = "abc=[def,ghi,jkl,mno]|def=[]|ghi=[def]|jkl=[ghi,stu]|mno=[]|pqr=[abc]|stu=[def]|vwx=[mno,ghi]|zy0=[jkl]".split("[|]");
Map<String, Set<String>> relationshipMap = new HashMap<>();
for (String entry : testMap) {
String[] keyValue = entry.split("[=]");
String replacement = keyValue[1].replaceAll("[^a-z0-9,]", "");
Set<String> valueSet = new HashSet<>();
String[] values = (!replacement.equals("") ? replacement.split("[,]") : new String[0]);
Collections.addAll(valueSet, values);
relationshipMap.put(keyValue[0], valueSet);
}
Set<String> sortedKeys = new TreeSet<>(new RelationshipComparator(relationshipMap));
sortedKeys.addAll(relationshipMap.keySet());
for (String key : sortedKeys) {
System.out.println(key + "=" + relationshipMap.get(key));
}
}
static class RelationshipComparator implements Comparator<String> {
private Map<String, Set<String>> relationshipMap;
RelationshipComparator(Map<String, Set<String>> relationshipMap) {
this.relationshipMap = relationshipMap;
}
@Override
public int compare(String o1, String o2) {
Set<String> o1Set = relationshipMap.get(o1);
Set<String> o2Set = relationshipMap.get(o2);
if (o1Set != null && o2Set != null) {
if (o1Set.size() == 0 && o2Set.size() > 0) {
printCompare(o1, o2, "o1Set.size() == 0: -1");
return -1;
}
if (o2Set.size() == 0 && o1Set.size() > 0) {
printCompare(o1, o2, "o2Set.size() == 0: 1");
return 1;
}
if (o1Set.contains(o2)) {
printCompare(o1, o2, "o1Set.contains(o2): 1");
return 1;
}
if (o2Set.contains(o1)) {
printCompare(o1, o2, "o2Set.contains(o1): -1");
return -1;
}
}
printCompare(o1, o2, "default: " + o1.compareTo(o2));
return o1.compareTo(o2);
}
private void printCompare(String o1, String o2, String result) {
System.out.println("**********");
System.out.println("o1: " + o1 + "=" + relationshipMap.get(o1));
System.out.println("o2: " + o2 + "=" + relationshipMap.get(o2));
System.out.println("result: " + result);
System.out.println("**********");
System.out.println();
}
}
}
如果運行代碼,將看到以下輸出:
def=[]
mno=[]
ghi=[def]
jkl=[stu, ghi]
abc=[def, ghi, jkl, mno]
pqr=[abc]
stu=[def]
vwx=[ghi, mno]
zy0=[jkl]
這是不正確的,因為“ jkl”引用了“ stu”,但是“ stu”在“ jkl”之后排序。
任何幫助將不勝感激。
您說關系是單向的,排除了明顯的情況,例如:
a=[b]
b=[a]
為此無法解決。 但是,我們還需要排除周期性關系,例如:
a=[b]
b=[c]
c=[a]
如果是這種情況,那么我相信您可以通過使用PriorityQueue
通過與鍵相關的值集的大小對鍵進行排序來實現所需的排序。 當密鑰從隊列中刪除時,它們也必須從包含它們的任何相關值集中刪除。 可以從反向Map<String, Set<String>>
恢復哪些值集包含給定鍵,該反向Map<String, Set<String>>
保存引用給定值鍵的鍵集。
希望一些代碼可以使事情變得更清楚:
static List<String> orderByRef(Map<String, Set<String>> relationshipMap)
{
final Map<String, Set<String>> relationshipMapCopy = new HashMap<>();
for(String key : relationshipMap.keySet())
relationshipMapCopy.put(key, new HashSet<>(relationshipMap.get(key)));
final Map<String, Set<String>> referencedBy = new HashMap<>();
for(String key : relationshipMap.keySet())
referencedBy.put(key, new HashSet<>());
for (Entry<String,Set<String>> e : relationshipMapCopy.entrySet())
for(String v : e.getValue())
referencedBy.get(v).add(e.getKey());
PriorityQueue<String> pq = new PriorityQueue<>(new Comparator<String>()
{
@Override
public int compare(String k1, String k2)
{
return relationshipMapCopy.get(k1).size() - relationshipMapCopy.get(k2).size();
}
});
pq.addAll(relationshipMap.keySet());
List<String> orderedKeys = new ArrayList<>();
while(!pq.isEmpty())
{
String minKey = pq.poll();
if(!relationshipMapCopy.get(minKey).isEmpty())
{
// cyclic relationship
break;
}
orderedKeys.add(minKey);
for(String refKey : referencedBy.get(minKey))
{
// remove minKey from value set of refKey
relationshipMapCopy.get(refKey).remove(minKey);
// reorder refKey in pq
pq.remove(refKey);
pq.add(refKey);
}
}
return orderedKeys;
}
請注意,由於我們通過從值集中刪除鍵來修改relationshipMap
,因此我們首先需要創建一個深層副本。 另外,我們可以通過檢查min鍵的值集是否為空來檢測是否存在循環關系。
輸出:
def []
mno []
stu [def]
ghi [def]
vwx [ghi, mno]
jkl [stu, ghi]
zy0 [jkl]
abc [def, ghi, jkl, mno]
pqr [abc]
這滿足了在鍵出現在列表中之前沒有引用任何鍵的約束。
對於包含循環關系的輸入,例如(z=[y]|y=[]|a=[b]|b=[c]|c=[a])
,我們得到:
y []
z [y]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.