【问题标题】:Filter generic collection with lamba使用 lambda 过滤通用集合
【发布时间】:2018-11-06 12:03:34
【问题描述】:

最后改写问题
------------------------------------------

我必须编写两个泛型方法来过滤泛型类型的集合。

首先从 Collection。

Collection 中的第二个 removeDuplicates 和可选的空/空白。

private <T extends Collection<C>, C extends Object> T  removeDuplicatesFromCollection(T collection, boolean skipNull)
{
    if(collection == null)
    {
        return collection;
    }

    final HashSet<C> tmp = new HashSet<C>();

    for(C element : collection)
    {
        if(element == null && skipNull)
        {
            continue;
        }
        else
        {
            tmp.add(element);
        }
    }

    collection.clear();
    collection.addAll(tmp);

    return collection;
}

我不知道如何解决这个问题。所以我使用 HashSet 来过滤唯一性,清除原始集合并添加 hashset 的所有条目。

首先我尝试通过流式传输和使用 lambda 表达式来解决这个问题,但这里也遇到了需要使用 collect() 处理的问题

首先我通过流式传输和使用 lambda 表达式尝试了这个,但没有 g

它有效,但我相信一定有更好的方法。

第一种方法是从 Collection 中删除重复项和空条目。 我想用 lambda 指令和 apache StringUtils 再次过滤这个列表,但我不知道如何处理收集和泛型类型 T。

private <T extends Collection<String>> T removeDuplicationsFromStringCollection(T collection, boolean skipNull, boolean skipEmpty)
{
    if(collection == null)
    {
        return collection;
    }

    T removedDuplicates = removeDuplicatesFromCollection(collection, skipNull);

    if(removedDuplicates == null || !skipEmpty)
    {
        return removedDuplicates;
    }
    else
    {
        removedDuplicates.().filter(s -> StringUtils.isNotBlank(s)).collect(T::new); <== [X Cannot instantiate the type T ]
    }
}

任何人都可以帮助完成这项工作吗? 如果有人对第一种方法有更好的解决方案,我也将不胜感激,因为我自己还没有完全满意。

非常感谢

------------[编辑]----

我不知道方法输入的集合类型,输出类型必须是输入类型的集合。 HashSet => 方法 => HashSet => 返回 ArrayList => 方法 => ArrayList => 返回

我不喜欢修改输入集合,但这是我保留集合类型的唯一方法。 问题不是在流式传输期间过滤列表,而是流式结束时的 collect(),并根据输入类型创建泛型类型。 问题是为输出集合保留输入集合的类型。

-------------[改写]-----
让我重新表述并简化这个问题,也许我不应该围绕我想做的事情写这么多。 有任何有用的提示和技巧,但核心问题没有解决

private <T extends Collection> T  doFilter(T pCollection, ....
{
    /** removed code for simplifing **/
    return pCollection.stream()
    /*.filter(....)*/
    .collect(  Collectors.toCollection( ?>>  T  <<?? ) );
}

谁能告诉我如何在泛型类型的 lambda 中收集(Collectors.toCollection(T))?

所以 列表 => doFilter() => 列表
ArrayList => doFilter() => ArrayList
Set => doFilter() => Set
LinkedHashSet => doFilter() => LinkedHashSet

[T => doFilter() => T]
....

【问题讨论】:

  • C extends Object 是多余的,顺便说一句。一切都扩展了对象。
  • 你说得对。我已经直接接管了。谢谢。
  • 但是在这个改变之后,我没有用于 for 循环的 C,所以一切都必须是一个 Object,并且 Hashset 是用类型 obejct 实例化的。但我会确保列表包含一个通用对象。确定可以是对象,也可以是类车或客户。当然汽车和客户扩展了对象,一切都扩展了对象,但是我必须将所有内容都投射到对象中
  • Java 8 有“过滤”集合的想法,这就是你想要做的。 zeroturnaround.com/rebellabs/…。它不会修改原始集合(这通常是一种不好的做法),您只需将集合转换为流,然后应用 lambda 过滤掉您不想要的内容。
  • 对于您的信息,我说这是一种不好的做法,因为无论谁为您的函数提供了集合可能不希望它被修改,您正在返回一个集合(相同的集合?一个新的?)和这种修改往往会导致代码导致并发问题。通常最好避免修改您的输入参数,除非您有特定的理由这样做(例如低内存嵌入式系统、大型数据集)

标签: java generics lambda


【解决方案1】:

使用 java 8 流式 API 确实可以实现更简洁的实现:

public <T extends Collection<Object> T removeDuplicatesFromCollectionAndFilterNull(T collection) {
  return collection.stream()
    .filter(Objects::isNull) // Method Reference to Objects.isNull(object)
    .collect(Collectors.toSet()); // Collect to set to remove duplicates
}

可以使用类似的方法来解决第二个问题。请注意,collect 方法不接受 lambda 来创建集合(并且 T::new 不起作用,因为 Java 不知道该方法是否具有默认构造函数),而是一个 Collector。 This guide might help

【讨论】:

  • 不错的答案,但这无法识别 Null 的可选过滤器。 Collectors.toSet 非常好,我将通过过滤器修改它(带有可选的布尔标志)
  • 是的,您可以使用任何 Predicate 来实现过滤器,因此自定义过滤器实现应该非常容易。例如.filter(obj -> skipBoolean ? false : Objects.isNull(obj))
  • 得到类型不匹配:无法从 Set 转换为 T
  • 是的,java 在运行时擦除集合中元素的类型。 This stackoverflow post 可能会对您有所帮助。
【解决方案2】:

你可以在 Stream 上使用 distinct() 方法

 Collection<String> list = Arrays.asList("A", "B", "C", "D", "A", "B", "C");
   // Get collection without duplicate i.e. distinct only
    List<String> distinctElements = 
    list.stream().distinct().collect(Collectors.toList());

这将返回所有不为空的不同元素:

public <T extends Collection<Object>> T removeDuplicatesFromCollection(T collection) {
        return  collection.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());
        }

您也可以将此示例与供应商一起使用:

  public static <T,U extends Collection<T>> U removeDuplicatesFromCollection(Iterable<T> iterable , Supplier<U> collectionType) {
        return  StreamSupport.stream(iterable.spliterator(),false).filter(Objects::nonNull).distinct().collect(Collectors.toCollection(collectionType));
    }
  List<String> strings = Arrays.asList("A","B",null,"B");
        removeDuplicatesFromCollection(strings, ArrayList::new).forEach(System.out::println);

【讨论】:

  • 但我不知道,如果该集合是一个列表,集合或什么的。我必须返回集合的 genericInputType,以便 Imputtype 匹配 outputtype
  • 你可以使用 Collectors.toCollection(Supplier supplier) 我在我的答案中更新了我的示例,你可以检查一下)
  • 感谢您的回答,但我们有一些已经在使用的实现。我必须彻底检查它们。我们有一些用于 input 的方法,一些用于 input 的方法,...。这些方法都在做相同的事情,并且仅在集合类型上有所不同。就是这样......我会将所有这些方法合并到这个通用方法中,这样我就可以替换旧的多次重复的东西。
  • 添加供应商,导致所有实现中的所有方法都必须修改,以添加和使用新参数
  • 你可以在你的接口中定义这样实现的默认方法并调用它也许对代码不会有这样的影响?
【解决方案3】:

我认为主要问题是要以某种方式知道您正在处理哪种特定类型的集合,因此您可以创建此特定类型的新集合并将过滤后的值添加到其中。改变你的论点是不好的做法,即你不应该修改收到的集合。

所以我会使用Supplier&lt;T&gt; 作为工厂来创建您的特定收藏:

private <T extends Collection<C>, C> T removeDuplicatesFromCollection(
        T collection, 
        boolean skipNull,
        Supplier<? extends T> factory) {

    if (collection == null) return null;

    // Remove duplicates, but keep insertion order (that's what LinkedHashSet is for)
    Set<C> noDuplicates = new LinkedHashSet<>(collection);

    // Remove nulls from the set if the flag is on
    if (skipNull) noDuplicates.removeIf(Objects::isNull);

    // Use the factory to create an empty instance of the collection 
    T newCollection = factory.get();

    // Add the no duplicates set, possibly without nulls
    newCollection.addAll(noDuplicates);

    return newCollection;
}

现在你可以使用上面的方法如下:

List<Integer> list = Arrays.asList(1, 2, 3, null, 5, 6, 1);

ArrayList<Integer> noDupsNoNulls = removeDuplicatesFromCollection(
        list,
        true,
        ArrayList::new); // [1, 2, 3, 5, 6]

编辑:如果您已经有对每种具体集合类型执行相同操作的方法,即一个用于List&lt;C&gt;,另一个用于Set&lt;C&gt;,等等,您不需要更改对这些方法的所有调用。您需要做的就是更改它们的实现,以便它们现在指向上面的新方法。

例如,假设你已经有了以下方法:

private <C> HashSet<C> removeDuplicatesFromCollection(
        HashSet<C> collection, 
        boolean skipNull) {

    // .....

}

您可以按如下方式更改:

private <C> HashSet<C> removeDuplicatesFromCollection(
        HashSet<C> collection, 
        boolean skipNull) {

    return removeDuplicatesFromCollection(collection, skipNull, HashSet::new);
}

如果你有 ArrayList 的另一种方法:

private <C> ArrayList<C> removeDuplicatesFromCollection(
        ArrayList<C> collection, 
        boolean skipNull) {

    // .....

}

您可以按如下方式更改:

private <C> ArrayList<C> removeDuplicatesFromCollection(
        ArrayList<C> collection, 
        boolean skipNull) {

    return removeDuplicatesFromCollection(collection, skipNull, ArrayList::new);
}

【讨论】:

  • 感谢您的回答,但我们有一些已经在使用的实现。我必须彻底检查它们。我们有一些用于 input 的方法,一些用于 input 的方法,...。这些方法都在做相同的事情,并且仅在集合类型上有所不同。就是这样......我会将所有这些方法合并到这个通用方法中,这样我就可以替换旧的多次重复的东西。添加供应商,导致必须修改所有实现中的所有方法,以添加和使用新参数
猜你喜欢
  • 1970-01-01
  • 2020-05-20
  • 2014-11-27
  • 2013-03-06
  • 2021-01-04
  • 1970-01-01
  • 2017-01-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多