2011-01-19 76 views
1

我已經寫了一個通用的分區類(一個分區是一個集合劃分爲不相交的子集,稱爲部分)。在內部,這是一個Map<T,Integer>和一個Map<Integer,Set<T>>,其中整數是部件的標籤。例如partition.getLabel(T t)給出t所在部分的標籤,並且partition.move(T t, Integer label)將t移動到由標籤標記的分區(在內部,它更新兩個地圖)。removeAll似乎影響其參數

但我的方法將對象的集合移動到一個新的部分不起作用。看來Set.removeAll()正在影響它的論點。我認爲這個問題就像是一個ConcurrentModificationException,但我無法解決這個問題。對不起,代碼很長,但我已經標記出問題的位置(大約一半的時間了),底部的輸出應該清楚問題是什麼 - 最後分區處於非法狀態。

import java.util.*; 

    public class Partition<T> { 
    private Map<T,Integer> objToLabel = new HashMap<T,Integer>(); 
    private Map<Integer,Set<T>> labelToObjs = 
     new HashMap<Integer,Set<T>>(); 
    private List<Integer> unusedLabels; 
    private int size; // = number of elements 

    public Partition(Collection<T> objects) { 
     size = objects.size(); 
     unusedLabels = new ArrayList<Integer>(); 
     for (int i = 1; i < size; i++) 
     unusedLabels.add(i); 
     // Put all the objects in part 0. 
     Set<T> part = new HashSet<T>(objects); 
     for (T t : objects) 
     objToLabel.put(t,0); 
     labelToObjs.put(0,part); 
    } 

    public Integer getLabel(T t) { 
     return objToLabel.get(t); 
    } 
    public Set<T> getPart(Integer label) { 
     return labelToObjs.get(label); 
    } 
    public Set<T> getPart(T t) { 
     return getPart(getLabel(t)); 
    } 

    public Integer newPart(T t) { 
     // Move t to a new part. 
     Integer newLabel = unusedLabels.remove(0); 
     labelToObjs.put(newLabel,new HashSet<T>()); 
     move(t, newLabel); 
     return newLabel; 
    } 
    public Integer newPart(Collection<T> things) { 
     // Move things to a new part. (This assumes that 
     // they are all in the same part to start with.) 
     Integer newLabel = unusedLabels.remove(0); 
     labelToObjs.put(newLabel,new HashSet<T>()); 
     moveAll(things, newLabel); 
     return newLabel; 
    } 
    public void move(T t, Integer label) { 
     // Move t to the part "label". 
     Integer oldLabel = getLabel(t); 
     getPart(oldLabel).remove(t); 
     if (getPart(oldLabel).isEmpty()) // if the old part is 
     labelToObjs.remove(oldLabel); // empty, remove it 
     getPart(label).add(t); 
     objToLabel.put(t,label); 
    } 
    public void moveAll(Collection<T> things, Integer label) { 
     // Move all the things from their current part to label. 
     // (This assumes all the things are in the same part.) 
     if (things.size()==0) return; 

     T arbitraryThing = new ArrayList<T>(things).get(0); 
     Set<T> oldPart = getPart(arbitraryThing); 

    // THIS IS WHERE IT SEEMS TO GO WRONG ////////////////////////// 
     System.out.println(" oldPart = " + oldPart); 
     System.out.println(" things = " + things); 
     System.out.println("Now doing oldPart.removeAll(things) ..."); 
     oldPart.removeAll(things); 
     System.out.println(" oldPart = " + oldPart); 
     System.out.println(" things = " + things); 

     if (oldPart.isEmpty()) 
     labelToObjs.remove(objToLabel.get(arbitraryThing)); 

     for (T t : things) 
     objToLabel.put(t,label); 
     getPart(label).addAll(things); 
    } 

    public String toString() { 
     StringBuilder result = new StringBuilder(); 
     result.append("\nPARTITION OF " + size + " ELEMENTS INTO " + 
     labelToObjs.size() + " PART"); 
     result.append((labelToObjs.size()==1 ? "" : "S") + "\n"); 
     for (Map.Entry<Integer,Set<T>> mapEntry : 
     labelToObjs.entrySet()) { 
     result.append("PART " + mapEntry.getKey() + ": "); 
     result.append(mapEntry.getValue() + "\n"); 
     } 
     return result.toString(); 
    } 

    public static void main(String[] args) { 
     List<String> strings = 
     Arrays.asList("zero one two three".split(" ")); 

     Partition<String> p = new Partition<String>(strings); 
     p.newPart(strings.get(3)); // move "three" to a new part 
     System.out.println(p); 

     System.out.println("Now moving all of three's part to the " + 
     "same part as zero.\n"); 
     Collection<String> oldPart = p.getPart(strings.get(3)); 
     //oldPart = Arrays.asList(new String[]{"three"}); // works fine! 
     p.moveAll(oldPart, p.getLabel(strings.get(0))); 
     System.out.println(p); 
    } 
    } 

/* OUTPUT 
PARTITION OF 4 ELEMENTS INTO 2 PARTS 
PART 0: [two, one, zero] 
PART 1: [three] 

Now moving all of three's part to the same part as zero. 

oldPart = [three] 
things = [three] 
Now doing oldPart.removeAll(things) ... 
oldPart = [] 
things = [] 

PARTITION OF 4 ELEMENTS INTO 1 PART 
PART 0: [two, one, zero] 
*/ 

回答

0

用我的調試器,我把在之前的removeAll一個斷點,我可以看到(我懷疑),其oldPart和事物一樣集合,以便移除所有元素清除該集合。

+0

謝謝。如果有人能夠很容易地看到我需要做什麼來修復這個課程,我會非常感激。我也會嘗試自己解決這個問題。 – Edd 2011-01-19 15:37:08

0

您的代碼非常混亂,但據我所知,oldPartthings實際上是同一個對象。 Set.removeAll(),因爲它是在調用並不影響它的參數,除非它是同一個對象:

public boolean removeAll(Collection<?> c) { 
    boolean modified = false; 

    if (size() > c.size()) { 
     for (Iterator<?> i = c.iterator(); i.hasNext();) 
      modified |= remove(i.next()); 
    } else { 
     for (Iterator<?> i = iterator(); i.hasNext();) { 
      if (c.contains(i.next())) { 
       i.remove(); 
       modified = true; 
      } 
     } 
    } 
    return modified; 
} 

更新:

最簡單的方法來消除,這是在moveAll()使用的things副本方法。事實上,這樣的副本已經存在。

T arbitraryThing = new ArrayList<T>(things).get(0); 

該行創建但立即丟棄things的副本。所以,我建議用替換它:

ArrayList<T> thingsToRemove = new ArrayList<T>(things) 
T arbitraryThing = thingsToRemove.get(0); 

而且在方法的其餘部分,則更換以thingsToRemovethings所有引用。

+0

謝謝。他們是同一個對象會解釋它。任何人都可以提出我需要做什麼來解決它?我不想複製'東西'。 (對不起,代碼很混亂,如果我有更多的評論,或者它只是構思不好或寫得不好,我無法弄清楚如何修正縮進。) – Edd 2011-01-19 15:35:33

相關問題