2011-11-30 51 views
4

此問題一直在困擾着我。抽象地講,無論語言,常常出現的情況下,當你想有這樣的方法:從抽象集合中產生抽象集合

Collection method(Collection c) { 
    // select some elements from c based on some filter 
    // and return a new collection 
} 

現在,Collection在這種情況下,一些抽象類(好比說在C#IListList在Java中)與幾個實現。我一直在想,產生抽象集合的正確過程究竟是什麼?

可以在方法中創建一個具體的集合並返回它嗎?像:

Collection method(Collection c) { 
    Collection cc = new ConcreteCollection(); 
    // select some elements from c based on some filter 
    return cc; 
} 

這當然穿的結果集合的約束,並會產生問題的情況下,出於某種原因,我們想要的方法的結果轉換爲不同的具體集合比內部使用的一個方法。

或者,使用反射來確定實際的具體的C型和創建類的實例:

Collection method(Collection c) { 
    Collection cc = c.getClass().newInstance(); 
    // select some elements from c based on some filter 
    return cc; 
} 

出於某種原因,這似乎不是很「優雅」給我。我非常感謝在這個問題上的一些見解。

回答

4

(說到java)。你返回Collection(一個接口)而不是具體類型(比如ArrayList)的原因是你告訴用戶他們不應該關心實際使用的具體類型是什麼。這使您可以自由選擇適合您的庫/ api的類型。

如果你正在執行一個特定的具體類,那麼你應該返回具體的類,而不是界面。

因此,他們不應該將您的返回類型轉換爲除Collection之外的任何其他類型。見 When should I return the Interface and when the concrete class?

+0

確實,我一直在問自己:「爲什麼你想把它轉換成別的東西?」。在這種情況下,似乎第一個版本應該沒問題。 – Tudor

0

調用者應該假定返回給定類型的Collection。

相反,它應該複製到所需的類型或通過所需的類型。

例如

Set<T> set2 = new HashSet<T>(filter(set)); 
List<T> list2 = new ArrayList<T>(filter(list)); 

filter(set2, set); // the target collection is passed. 
filter(list2, list); 
0

至於我能理解,你想知道如何使接受通用的列表並返回另一個修改的泛型列表的方法。

因此,我的建議是使用實現方法來修改其狀態的抽象類型。

IList<object> list = new List<object>(); 

list.Add(new object()); 
list.Remove(obj); 

或者像上面顯示,實例化一個實現IList列表(或相當於Java)與此實例工作,如果你想返回的結果爲IList

編輯

從列表中過濾一些項目到一個新的,泛型可以幫助(我不知道這個功能是否存在於Java中)。

public IList<T> Filter<T>(IList<T> list) 
    { 
     var result = new List<T>(); 
     result.Add(list[0]); // Or whatever filtering method 
     return result; 
    } 
0

對於ConcreteCollection的問題,這是絕對允許的。
爲了解預期具有不同的混凝土收集情況,有幾種方法可以解決該問題:

更改方法的返回類型。例如:

ConcreteCollection method(Collection c){ 
    ConcreteCollection cc=new ConcreteCollection 
    for(Object x: c){ 
     //do something 
    } 
    return cc 
} 

利用多態性。示例:

Collection x=method(c) 
x.add(new Object) //add is a method defined within the abstract Collection 

使用一些實用程序來投射類型。例如:

LinkedList h=Collections.toLinkedList(method(c)) 

希望我的回答有幫助。 ^^

1

您可以採取的一種方法是創建一個Collection實現,將呼叫委託給原始Collection。這推遲了過濾大型Collection的潛在昂貴操作,直到您需要顯式讀取元素爲止。它還可以節省內存。

public interface Filter<T> { 
    boolean include(T t); 
} 

public class FilterCollection<T> implements Collection<T> { 
    private final Collection<T> orig; 
    private final Filter<T> filter; 

    public FilterCollection(Collection<T> orig, Filter<T> filter) { 
    this.orig = orig; 
    this.filter = filter; 
    } 

    public int size() { 
    int sz = 0; 

    for (T t : orig) { 
     if (filter.include(t)) { 
     ++sz; 
     } 
    } 

    return sz; 
    } 

    public boolean contains(Object o) { 
    return o instanceof T && filter.include((T) o) && orig.contains(o); 
    } 

    public boolean add(T t) { 
    if (!filter.include(t)) { 
     throw new IllegalArgumentException("Element lies outside filter bounds."); 
    } 

    orig.add(t); 
    } 
} 
0

如果你想你的方法接受盡可能多的不同的集合類型越好,你要肯定的是,結果是一樣的實現類型,你放什麼東西,你可能想要使用void方法直接修改提供的集合。例如:

import com.google.common.base.Predicate; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Iterator; 
import java.util.List; 

public class Testy { 

    private static <T> void filter(Iterable<T> collection, Predicate<T> filter) { 
     Iterator<T> iterator = collection.iterator(); 
     while (iterator.hasNext()) { 
      if (!filter.apply(iterator.next())) { // Condition goes here 
       iterator.remove(); 
      } 
     } 
    } 

    public static void main(String... args) { 
     List<String> list = new ArrayList<String>(); 
     list.addAll(Arrays.asList("A", "B", "C", "D")); 

     filter(list, new Predicate<String>() { // Anonymous filter (predicate) 
      @Override public boolean apply(String input) { 
       return input.equals("B"); 
      } 
     }); 

     System.out.println(list); // Prints ["B"] 
    } 

} 

輔助方法filter需要一個Iterable,用於遍歷的東西所需要的最簡單的類型。將篩選器應用於每個元素,並且如果謂詞(篩選器)返回false,請使用Iterator.remove()從底層集合中刪除該元素。

這裏的Predicate<T>界面來自谷歌。如果您不想導入它,您可以輕鬆編寫自己的文件。唯一需要的方法是返回布爾值的apply(T)。要麼是這樣,要麼直接在循環內寫入條件並擺脫第二個參數。

如果您的原始集合是可變的,並且您不希望保留任何中間結果,則此方法效率最高。

另一個選擇是使用Google Collections Collections2.filter(Collection<E>, Predicate<E>),它將返回Collection<E>,就像在您的問題中一樣。同樣,Iterables類也會做同樣的事情,但創建惰性迭代器,其中只有在實際進行迭代時才應用過濾器。

3

在Java中,實際上在java.util.Collections類中有一些很好的例子。關鍵方法不是採用Collection並返回Collection,而是採用兩個集合,即「src」和「dest」。例如,看copy方法的簽名:

public static <T> void copy(List<? super T> dest, List<? extends T> src) 

這使實例呼叫者的目的地列表的責任。

我想你可以做同樣的事情,當你想創建一個方法,作用於src集合並將結果放入目標集合(而不是列表)。

我同意Matthew Farwell的回答,你可能只想返回接口並使用它,但是對於你真的需要使用特定實現類的時間,你可以使用類Collections類它。

+0

嗯......我喜歡這個建議。 – Tudor