2011-11-30 50 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