使用lamba过滤通用集合

时间:2018-11-06 12:03:35

标签: java generics lambda

结尾处的疑问句
------------------------------------------- -

我必须编写两个通用方法,以过滤通用类型的Collections。

首先从Collection <中删除removeDuplicates和可选的null扩展对象>。

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

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来过滤唯一性,清除origin集合并添加hashset的所有条目。

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

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

它可以工作,但是我敢肯定必须有更好的方法。

第一种方法是从Collection中删除重复项和空条目。 我倾向于使用lambda指令和apache StringUtils再次过滤此列表,但我不知道如何处理collect和泛型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))吗?

所以 List => doFilter()=> List
ArrayList => doFilter()=> ArrayList
设置<联系人> => doFilter()=>设置<联系人>
LinkedHashSet => doFilter()=> LinkedHashSet

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

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

您可以在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());

这将返回所有非null的不同元素:

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);

答案 2 :(得分:0)

我认为主要问题是要以某种方式知道您要处理的是哪种特定类型的集合,因此您可以创建此特定类型的新集合并将过滤后的值添加到其中。更改参数是不明智的做法,也就是说,您不应该修改收到的集合。

因此,我将使用Supplier<T>作为工厂来创建您的特定集合:

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<C>,另一种用于Set<C>,等等,那么您将不会无需将所有调用更改为这些方法。您所需要做的就是更改其实现,以便它们现在指向上面的新方法。

例如,假设您已经具有以下方法:

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);
}