当前位置: 首页 > 知识库问答 >
问题:

Java Lambda Stream Distinct()在任意键上?[副本]

戚哲
2023-03-14

下面是它的名称

BigDecimal totalShare = orders.stream().map(c -> Pairing.keyLeft(c.getCompany().getId(), c.getShare())).distinct().map(Pairing::getRightItem).reduce(BigDecimal.ZERO, (x,y) -> x.add(y));

这是配对类

    public final class Pairing<X,Y>  {
           private final X item1;
           private final Y item2;
           private final KeySetup keySetup;

           private static enum KeySetup {LEFT,RIGHT,BOTH};

           private Pairing(X item1, Y item2, KeySetup keySetup) {
                  this.item1 = item1;
                  this.item2 = item2;
                  this.keySetup = keySetup;
           }
           public X getLeftItem() { 
                  return item1;
           }
           public Y getRightItem() { 
                  return item2;
           }

           public static <X,Y> Pairing<X,Y> keyLeft(X item1, Y item2) { 
                  return new Pairing<X,Y>(item1, item2, KeySetup.LEFT);
           }

           public static <X,Y> Pairing<X,Y> keyRight(X item1, Y item2) { 
                  return new Pairing<X,Y>(item1, item2, KeySetup.RIGHT);
           }
           public static <X,Y> Pairing<X,Y> keyBoth(X item1, Y item2) { 
                  return new Pairing<X,Y>(item1, item2, KeySetup.BOTH);
           }
           public static <X,Y> Pairing<X,Y> forItems(X item1, Y item2) { 
                  return keyBoth(item1, item2);
           }

           @Override
           public int hashCode() {
                  final int prime = 31;
                  int result = 1;
                  if (keySetup.equals(KeySetup.LEFT) || keySetup.equals(KeySetup.BOTH)) {
                  result = prime * result + ((item1 == null) ? 0 : item1.hashCode());
                  }
                  if (keySetup.equals(KeySetup.RIGHT) || keySetup.equals(KeySetup.BOTH)) {
                  result = prime * result + ((item2 == null) ? 0 : item2.hashCode());
                  }
                  return result;
           }

           @Override
           public boolean equals(Object obj) {
                  if (this == obj)
                         return true;
                  if (obj == null)
                         return false;
                  if (getClass() != obj.getClass())
                         return false;
                  Pairing<?,?> other = (Pairing<?,?>) obj;
                  if (keySetup.equals(KeySetup.LEFT) || keySetup.equals(KeySetup.BOTH)) {
                         if (item1 == null) {
                               if (other.item1 != null)
                                      return false;
                         } else if (!item1.equals(other.item1))
                               return false;
                  }
                  if (keySetup.equals(KeySetup.RIGHT) || keySetup.equals(KeySetup.BOTH)) {
                         if (item2 == null) {
                               if (other.item2 != null)
                                      return false;
                         } else if (!item2.equals(other.item2))
                               return false;
                  }
                  return true;
           }

    }

更新:

测试了斯图尔特下面的功能,它似乎工作很好。下面的操作区分每个字符串的第一个字母。我想要弄清楚的是,ConcurrentHashMap如何只为整个流维护一个实例

public class DistinctByKey {

    public static <T> Predicate<T> distinctByKey(Function<? super T,Object> keyExtractor) {
        Map<Object,Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    public static void main(String[] args) { 

        final ImmutableList<String> arpts = ImmutableList.of("ABQ","ALB","CHI","CUN","PHX","PUJ","BWI");

        arpts.stream().filter(distinctByKey(f -> f.substring(0,1))).forEach(s -> System.out.println(s));
    }

输出为...

ABQ
CHI
PHX
BWI

共有1个答案

季凡
2023-03-14

Distinct操作是一个有状态的流水线操作;在本例中,它是一个有状态的过滤器。自己创建这些有点不方便,因为没有内置的东西,但是一个小的helper类应该可以做到这一点:

/**
 * Stateful filter. T is type of stream element, K is type of extracted key.
 */
static class DistinctByKey<T,K> {
    Map<K,Boolean> seen = new ConcurrentHashMap<>();
    Function<T,K> keyExtractor;
    public DistinctByKey(Function<T,K> ke) {
        this.keyExtractor = ke;
    }
    public boolean filter(T t) {
        return seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
}

我不知道您的领域类,但我认为,有了这个helper类,您可以像下面这样做:

BigDecimal totalShare = orders.stream()
    .filter(new DistinctByKey<Order,CompanyId>(o -> o.getCompany().getId())::filter)
    .map(Order::getShare)
    .reduce(BigDecimal.ZERO, BigDecimal::add);

不幸的是,类型推断在表达式中不能得到足够的深入,所以我不得不显式地为DistinctByKey类指定类型参数。

可以去掉k类型参数,因为除了存储在映射中之外,它实际上并不用于其他任何用途。因此object就足够了。

/**
 * Stateful filter. T is type of stream element.
 */
static class DistinctByKey<T> {
    Map<Object,Boolean> seen = new ConcurrentHashMap<>();
    Function<T,Object> keyExtractor;
    public DistinctByKey(Function<T,Object> ke) {
        this.keyExtractor = ke;
    }
    public boolean filter(T t) {
        return seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
}

BigDecimal totalShare = orders.stream()
    .filter(new DistinctByKey<Order>(o -> o.getCompany().getId())::filter)
    .map(Order::getShare)
    .reduce(BigDecimal.ZERO, BigDecimal::add);

这稍微简化了事情,但我仍然必须为构造函数指定类型参数。尝试使用diamond或静态工厂方法似乎并不能改善事情。我认为困难在于,当构造函数或静态方法调用在方法引用的实例表达式中时,编译器无法推断泛型类型参数。哦好吧。

(可能会简化它的另一个变体是使DistinctByKey 实现谓词 并将方法重命名为Eval。这将消除使用方法引用的需要,并可能改进类型推断。但是,它不可能像下面的解决方案那么好。)

public static <T> Predicate<T> distinctByKey(Function<? super T,Object> keyExtractor) {
    Map<Object,Boolean> seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

BigDecimal totalShare = orders.stream()
    .filter(distinctByKey(o -> o.getCompany().getId()))
    .map(Order::getShare)
    .reduce(BigDecimal.ZERO, BigDecimal::add);
 类似资料:
  • 问题内容: 我经常遇到Java lambda表达式的问题,当我想对对象的任意属性或方法上的stream()进行区分(但要保留该对象而不是将其映射到该属性或方法上)时,就会遇到问题。我开始创建容器,如这里讨论的那样,但是我开始做足够的工作,直到它变得令人讨厌并制作了许多样板课程。 我把这个Pairing类放在一起,它包含两种类型的两个对象,并允许你指定从左,右或两个对象上抠出的键。我的问题是……在某

  • 问题内容: 我将如何在Python中“按任意键”(或抓住菜单选项)? raw_input要求您按回车键。 Windows msvcrt具有getch()和getche()。 有使用标准库执行此操作的可移植方法吗? 问题答案:

  • 我们正在AWS上托管Cassandra 2.0.2群集。我们最近开始通过引导新节点和停用旧节点,从普通驱动器升级到SSD驱动器。除了两个节点永远处于退役状态外,进展相当顺利。现在,在新的6个节点运行后,我们注意到一些使用phpcassa的旧工具停止工作。安全组没有任何变化,所有端口TCP/UDP都是打开的,telnet可以通过9160连接,cqlsh可以“使用”群集,选择数据,但是,“描述群集”失

  • 我使用Apache Spark 2.2.0和Scala。 我以这个问题为指导,在不使用pivot函数的情况下透视数据帧。 我需要在不使用pivot函数的情况下透视数据帧,因为我有非数字数据,而< code>pivot只对数字数据使用聚合函数,如< code>sum 、< code>min 、< code>max。我想在< code>pivot聚合中使用一个非数字列。 这是我的数据: 我希望它按<

  • 在React中键的意义是什么?我读到在循环中使用索引不是键的最佳解决方案。为什么啊?

  • 我正在从我的go程序向API发出http请求。请求正文是一个 JSON 对象,如下所示: 其中XXXXXXXX-XXXX-XXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 如何定义一个允许该部分接