【问题标题】:Filter a Stream depending on String length and attribute根据字符串长度和属性过滤流
【发布时间】:2019-12-16 21:41:01
【问题描述】:

所以有 1 个类和 1 个枚举。枚举例如:

public enum E {
 E1, E2, E3
}

班级:

public class A {  
 private E e;
 private String name;

 public A (E e, String name){
  this.e = e;
  this.name = name;
 }
 public E getE(){
  return E;
 }
 public String getName(){
  return name;
 }
}

Given 是一个列表。 我想要的是根据他们的名字和他们的枚举属性过滤列表(带流)。与具有相同枚举属性的其他对象相比,仅筛选出没有最大名称长度的 A。

输出应该是一个 Map>。列出,因为可能存在多个具有相同名称长度的对象。

示例:

List<A> input = Arrays.asList(new A(E.E1, "abcd"), new A(E.E1, "xyz"), new A(E.E2, "abcd"), new A(E.E2, "wxyz"), new A(E.E3, "xyzb"));

这应该返回:

E1 = "abcd" // because "xyz" has length 3
E2 = "abcd", "wxyz" // as both have length 4, e.g. max length
E3 = "xyzb" // because it is the only object

我的一个解决方案是您可以将带有“groupingBy”的列表分组到它们各自的枚举组中。 最后,您将拥有例如 3 个带有 A 列表作为值的键。 最终,您只需删除名称短于最大名称长度的所有值。但仅限于他们各自的关键

【问题讨论】:

  • 您需要group by 功能来查找每个枚举值的统计信息。
  • 您应该提供示例输入和预期输出。

标签: java java-stream grouping


【解决方案1】:

您可以先使用嵌套的groupingBy 来确保按属性E 分组,然后再按namelength 分组。在按name 分组时,您可以收集到TreeMap,因为您只对名称长度为max 的条目感兴趣。这是为了确保您可以进一步将 E 映射到 TreeMap 的 lastEntry 的值。

上述方法的解决方案如下:

Map<E, List<A>> output = input.stream()
        .collect(Collectors.groupingBy(A::getE,
                Collectors.groupingBy(e -> e.getName().length(),
                        TreeMap::new, Collectors.toList()))) // Map<E, TreeMap<Integer, List<A>>> // the nested groupings
        .entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getKey,
                m -> m.getValue().lastEntry().getValue())); // only max length entry mapped here

【讨论】:

  • @a_ray_of_hope 注意:public String getName() { return name; } 的实现是返回String,而不是您的问题中的A。也就是说,这也取决于您如何收集输出,因为类型推断起着至关重要的作用。但是对于您当前的最小示例,我可以看到它适用于我的本地设置。如果没有详细说明它在哪里失败以及它读取了什么错误,就无法真正进一步说明。
【解决方案2】:

您可以尝试将列表转换为地图,然后获取过滤值

假设:A 具有以 E 和 Name 作为参数的构造函数。我为他们创建了吸气剂。

List<A> input = Arrays.asList(new A(E.E1, "abcd"), new A(E.E1, "xyz"), new A(E.E2, "abcd"), new A(E.E2, "xyz"), new A(E.E3, "xyzb"));

Collection<A> result = input.stream()
    .collect(Collectors.toMap(A::getE, Function.identity(), (n1, n2) -> (n1.getName().length() > n2.getName().length() ? n1 : n2)))
    .values();

System.out.println(result);

已根据 cmets 更新:解决两个字符串长度相同的情况。在单个流操作中处理并不直接。我们这里需要分工。

这是一种方法:

List<A> input = Arrays.asList(new A(E.E1, "abcd"), new A(E.E1, "xyzd"), new A(E.E2, "abcd"), new A(E.E2, "xyz"), new A(E.E3, "xyzb"));

Map<E, Integer> lengthMap = input
    .stream()
    .collect(Collectors.toMap(A::getE, t -> t.getName().length(), (n1, n2) -> Math.max(n1, n2)));

List<A> result = input.stream()
    .filter(a -> a.getName().length() == lengthMap.get(a.getE()))
    .collect(Collectors.toList());

for (A a : result) {
    System.out.println(a.getE() + " : " + a.getName());
}

输出:

E1 : abcd
E1 : xyzd
E2 : abcd
E3 : xyzb

【讨论】:

  • 啊,我明白你在那里做了什么。谢谢!但是这只返回了A的一个对象,如果有两个对象具有相同的枚举类型和相同的名称长度,则只会返回一个。您如何还返回具有最大长度的每个对象(与相同枚举类型的对象相比)? ://
  • @SunilDabburi 这并不能解决 OP 所述的 grouping 问题。它只确保选择列表中具有最大长度的所有元素。相反,过滤处于一个级别,其中过滤了基于类型 E 的列表中分组的所有元素。
  • 我不明白。我根据 E 对它们进行分组并存储每个 E 的最大长度。然后根据每个条目的每个 E 的长度过滤列表
猜你喜欢
  • 2013-11-25
  • 2019-02-04
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 2011-06-04
  • 2018-09-16
  • 2012-11-23
相关资源
最近更新 更多