2010-11-01 36 views
4

假設我有一個列表(EG:LinkedList<SomeObject>,其中包含由某個屬性排序的元素(EG:SomeObject.someValue())。此屬性通常可以並且通常經常重複/它不是是不是唯一的,但是從來不是空的。Java Collections:如何將已排序的列表劃分爲子列表

有沒有一種方便的方法將它分成多個列表,每個列表只包含其基數次序相等?此外,這可以只用一次迭代列表完成?例如,原始列表:

1, 1, 1, 2, 2, 3, 3, 3 

從該所期望的列表:

1, 1, 1 
2, 2, 
3, 3, 3 

回答

10

不太方便,但是:

  • 開始一個循環。存儲前一個項目,並將其與當前值進行比較。
  • 如果先前與當前不同(使用equals(..),並小心null),然後創建一個新的List,或使用list.subList(groupStart, currentIdx)
+1

+1 - 到目前爲止唯一正確的答案,IMO :-) – 2010-11-01 23:05:38

+1

+1,但如果它是一個數組,修改的二進制搜索(找到最後一個實例,而不是第一個)會更好大量數據) – st0le 2010-11-02 05:35:45

1

這應該工作(未經測試,但我敢肯定一切好吧,這也假設列表的內容是可排序的):

public static List[] getEquivalentSubLists(List parent) 
{ 

    List cloneList = parent.clone(); 
    Collections.sort(cloneList); 

    ArrayList<List> returnLists; 
    int end; 
    while (cloneList.size() > 0) 
    { 
     end = cloneList.lastIndexOf(cloneList.get(0)); 

     returnLists.add(cloneList.subList(0, end)); 
     cloneList.removeAll(cloneList.subList(0, end)); 
    } 

    return returnList.toArray(); 
} 
3

很確定沒有一個java API的方法。然而,你可以寫:

// This assumes your list is sorted according to someValue() 
// SomeValueType is the type of SomeObject.someValue() 
public Map<SomeValueType, List<SomeObject>> partition(List<SomeObject> list) { 
    Object currValue = null; 
    HashMap<SomeValueType, LinkedList<SomeObject>> result = new HashMap<SomeValueType, LinkedList<SomeObject>>(); 
    LinkedList<SomeObject> currList = null; 

    for (SomeObject obj : list) { 
     if (!obj.someValue().equals(currValue()) { 
      currValue = obj.someValue(); 
      currList = new LinkedList<SomeObject>(); 
      result.put(currValue, currList); 
     } 
     currList.add(obj); 
    } 
} 

這將返回子列表,一個HashMap其中關鍵是someValue和值是與之相關的分區列表。請注意,我沒有測試這個,所以不要複製代碼。

編輯:使這個返回hashmap而不是arraylist。

+0

您應該返回Map而不是HashMap,並且需要List而不是LinkedList。任何你在返回值中遺漏了一個'>'。 – whiskeysierra 2010-11-01 22:45:56

+0

@Willi editted。正如我所說我沒有測試任何這一點。 > _ < – shoebox639 2010-11-01 22:49:34

+0

Hashmap作爲輸出是必需的,因爲通過鍵更容易查找整個列表。它與輸入是否排序無關。如果你真的看看代碼,我會利用它被分類的事實。 – shoebox639 2010-11-02 00:36:33

4

你可以使用Apache CollectionUtils要做到這一點,在「名單」是最初的名單,而「價值」是要提取的子列表中的對象的當前值:

Collection<SomeObject> selectedObjects = CollectionUtils 
    .select(list, 
      new Predicate() { 
       boolean evaluate(Object input) { 
        return ((SomeObject) input).someValue().equals(value);  
       } 
      }); 

這種方法手段使用一個衆所周知的,經過充分測試的庫(這總是一件好事),但缺點是,您將爲每個需要的子列表遍歷列表一次。

+1

要將此應用於OP的陳述問題,您需要用代碼片段循環選擇值。結果是一個「O(N ** 2)」解決方案,它不使用輸入列表排序的事實。 Ughh。 – 2010-11-01 22:58:41

+0

@Stephen C:是的,這正是我想要在最後一句中表達的意思。如果輸入列表相對較小,這不是問題。但是如果它變大,另一種單循環方法會更好。我儘量避免預先優化(因爲它只是邪惡的根源)。 – crunchdog 2010-11-02 12:02:31

3

如果你想使用谷歌Guava-libaries

import com.google.common.collect.HashMultiset; 
import com.google.common.collect.Lists; 

public class Example { 
    public static void main(String[] args) { 
     HashMultiset<Integer> ints = HashMultiset.create(); 
     ints.addAll(Lists.newArrayList(1, 1, 1, 2, 2, 3, 3, 3)); 
     System.out.println(ints); 
    } 
} 

輸出:

[1 x 3, 2 x 2, 3 x 3] 

如果你需要計算的X,你必須使用ints.count(x);,如果你有值類型,你沒有多少個元素需要更多,然後纔算數。

+0

你用這個得到3個不同的列表嗎? – zengr 2010-11-02 05:12:46

+0

這是一組'Entry '而不是'Integer',你得到3個不同的Entry。 'ints.entrySet();'可以得到一組'Entry '。 – Margus 2010-11-02 05:33:15