【问题标题】:Java: select from collection only elements of provided typeJava:仅从集合中选择提供类型的元素
【发布时间】:2010-04-27 15:07:55
【问题描述】:

我有一个 B 和 C 类型元素的集合,它们都扩展了 A。我需要过滤集合以获取只有 B 类型的元素。

除了:

for (A a : initCollection) {
  if (a instanceof B) {
    newCollection.add(a)?
  }
}

谢谢

【问题讨论】:

  • 你为什么不喜欢它现在的制作方式?
  • 你也可以等待Java7。我想这可以用closures 来缩短。

标签: java collections


【解决方案1】:

在其他答案中提到了Guava,但没有提到具体的解决方案,这比人们意识到的还要简单:

Iterable<B> onlyBs = Iterables.filter(initCollection, B.class);

它简单干净,做正确的事,只创建一个实例并且不复制任何内容,并且不会引起任何警告。

(不过,Collections2.filter() 方法没有这种特殊的重载,所以如果你真的想要一个Collection,你必须提供Predicates.instanceOf(B.class),结果集合仍然很遗憾是Collection&lt;A&gt; 类型.)

【讨论】:

  • 请注意,使用此解决方案,每次迭代 overBs 时,都会执行过滤逻辑。这可能是一件好事(如果 initCollection 已更新并且您希望 onlyBs 反映更改)或坏事(如果您期望一个副本,或者将 onlyBs 传递给一些迭代它多次的代码)。如果你想要一个副本,只需从 filter() 的输出创建一个集合。
【解决方案2】:

按照您的示例,我认为这样做没有任何问题。然而,如果你想变得花哨,你可以使用谷歌的Guava library,特别是Collections2 类,对你的收藏进行函数式过滤。您可以提供自己的谓词,这当然可以做instanceof 的事情。

Collection<Object> newCollection = Collections2.filter(initCollection, new Predicate<Object>() { 
    public boolean apply(Object o) {
        return !(o instanceof String);
    } 
});

【讨论】:

  • 好的,谢谢。同样可以通过 apache 的 commons-collections 库来完成
  • @glaz:它只是不通用的。番石榴(谷歌收藏)是。我不会错过参数化类型。
  • 我是谷歌收藏的忠实粉丝。但我看不出谷歌收藏在这个特定示例中可以提供任何优势。会有更多的项目依赖,更多的代码行,更复杂的代码,几乎可以肯定它会更慢。而且没有任何优点。
  • @Roman:我提到番石榴只是作为一种选择;这主要是口味问题。但是您可能会惊讶地发现,Guava 解决方案提供了更好的 性能。 OP 的解决方案使用原始数据的子集创建新的数据结构;这是很多news 并为垃圾收集器工作。另一方面,Collections2 提供了原始集合的视图,即没有新的数据结构(除了一个小的固定成本支持数据结构,我敢打赌)。这是提高函数式编程效率的一个很好的例子。
  • @Carl Smotricz:您能否附上一个代码示例(或至少提及 Collection2 中的方法)来完成您所描述的事情?
【解决方案3】:

AFAIK,如果不向 A、B 和 C 类添加代码,就没有简单的方法可以做到这一点。 真正的问题是:为什么需要过滤掉特定类型的元素?这与 OOP 中子类型的想法背道而驰。

【讨论】:

  • 为什么会这样?我有一个超类型的集合,当然可以有不同的子类型实例。现在我想要准确的课程。为什么不?哪里错了?
  • 这不是一个错误。虽然 OO 纯粹主义者可能会说,对于您想要执行的任何操作,让主集合中的每个对象都实现一个方法来执行它(更高级别的对象实现一个 noop 方法),这经常会影响性能,以至于做什么你问的很频繁。
  • +1 这样做的愿望对我来说似乎是一种代码味道。在某些情况下它可能是正确的方法,但我认为在大多数情况下,更改设计会是更好的选择。
  • @glaz666:如果必须从超类型中过滤子类型,则暗示超类型设计不当。情况并非总是如此,但在许多情况下,无需使用反射即可解决问题:例如,使用访问者模式。
【解决方案4】:

如果您不想创建新集合,可以从现有集合中删除奇数元素:

Iterator<A> it = initCollection.iterator();
while (it.hasNext()){
   if (it.next() instanceof C) {
      it.remove();
   }
}

【讨论】:

    【解决方案5】:

    这是一个老问题,但我想我会添加这个以防有人发现它。

    您可以使用纯 Java 8 及其 Streams 来做到这一点。

    List<B> filtered = listOfA.stream()
        .filter(a -> a instanceof B)
        .map(a -> (B) a)
        .collect(Collectors.toList());
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-12
      • 1970-01-01
      • 2012-01-07
      • 1970-01-01
      • 2022-07-04
      • 2011-02-23
      • 2023-03-09
      相关资源
      最近更新 更多