Java中的Lambda表达式如何帮助排序?帮我了解

问题描述

这里的任务是我得到输入字符串,例如'tree'并根据频率对其进行排序,输出应为'eetr'/'eert',因为e重复2次且t,r频率为1。

我有这段代码,它遍历字符串是什么,将所有字符及其频率放入Hashmap中,并使用优先级队列对它们进行降序排序,然后在字符串生成器中获取结果,

但是我需要一些帮助来理解优先级队列参数中使用的lambda函数。这是我的功能

public String frequencySort(String s)          // lets assume that input string is "tree"
{
    HashMap<Character,Integer> map = new HashMap<>();
    for(char c:s.tochararray())
    {
        map.put(c,map.getorDefault(c,0) + 1);
    }                                                       //  Now after this my HashMap will have values (t,1),(r,(e,2)

PriorityQueue<Character> maxHeap = new PriorityQueue<>((a,b) -> map.get(b) - map.get(a));  //my question is about this constructor in priority queue,There are just two input parameters,how subtracting two things will help in sorting ??

maxHeap.addAll(map.keySet()); // i guess the keyset is Now 't,r,e'  but constructor has only 2 parameters,what is going on ? How will the constructor help in setting the sorting behavIoUr of prioroty queue? 

StringBuilder result = new StringBuilder();

    while(maxHeap.isEmpty())
    {
        char current = maxHeap.remove();
        for(int i=0;i < map.get(current);i++)
        {
            result.append(current);
        }
    }

}

有人可以使用函数中的“树”示例向我解释流程吗?

解决方法

但是构造函数只有2个参数

不,请仔细查看,在new PriorityQueue(...)中,...部分仅包含一件事-(a,b) -> map.get(b) - map.get(a)PriorityQueue<Character>的构造函数期望一个类型为Comparator<Character>的参数。 Comparator是一个对象,它可以比较两个对象并判断哪个是“更大的”或它们是否相等。优先级队列将使用此Comparator对其元素进行排序。

如果您发现lambda表达式令人困惑,那么如何将代码重写为:

new PriorityQueue<>(Comparator.comparing(map::get).reversed());

意图应该更清楚,并且做大致相同的事情。我们正在创建一个优先级队列,该队列应该通过比较在每个元素上应用map.get得到的整数来比较其元素。换句话说,在给定两个元素ab的情况下,队列应通过比较map.get(a)map.get(b)来决定哪个元素先出现。并且您想要反向升序(即降序)。

在您的原始代码中,不是使用返回comparing的{​​{1}}方法,而是使用了lambda表达式,该表达式基本上是compare方法的实现。如果您阅读了该方法的文档,将会看到应该:

  • 如果第一个参数“小于”第二个参数,则返回负整数
  • 如果参数相等,则返回0
  • 如果第一个参数比第二个参数“大”,则返回一个正整数

从数学上观察,如果Comparator<Character>map.get(a) < map.get(b)将为负。如果map.get(a) - map.get(b)map.get(a) > map.get(b)将为正,如果map.get(a) - map.get(b)map.get(a) = map.get(b)将为0。

现在,您应该了解为什么map.get(a) - map.get(b)通过比较将map.get(b) - map.get(a)应用于每个元素而获得的整数来给您反向顺序。

其他注释:

  • 使用减法来实现map.get实际上不是一个好主意,因为溢出。有关更多信息,请参见here
  • 您的代码有两个错别字/错误。 1.在末尾缺少Comparator语句2.循环控制条件应为return result.toString();。您错过了while(!maxHeap.isEmpty())
,

这与wordCount中的Scala示例非常相似。但是它计算字符串中的单词,但是您的程序计算单词中的字母。非常相似。

但是, 地图输出如下:

(t,1),(r,1),(e,2)

map.get(b) - map.get(a)仅用作比较器。如果map.get(b) - map.get(a)为负数,则表示map.get(b) < map.get(a)并在a之前进行排序。如果为正,则表示map.get(b) > map.get(a),并在a之后排序。如果为零,则它们相等。

See Javadoc for PriorityQueue :基于优先级堆的无限制优先级队列。优先级队列的元素根据其自然顺序进行排序,或者由队列构建时提供的Comparator进行 ,这取决于所使用的构造器。

,

我认为,像这样的“隐式”比较器的使用可能会非常令人困惑。 PriorityQueue构造函数(自Java 8起)被定义为将一个java.util.Comparator用作it参数。 Comparator接口指定一个函数compare(),该函数带有两个参数,并返回一个整数。整数的符号指示哪个元素较大。这就是为什么在OP中将两个元素的值相减的原因-如果相等,则结果将为零。如果一个大于另一个,则结果将为正或负。 PriorityQueue实现使用这些结果(应用于许多元素)来确定它们应放置的顺序。

在lamba表达式出现之前,我认为这种逻辑相当直观。现在,Comparator被声明为“功能接口”。这意味着可以直接从lamba表达式创建接口的匿名实例化。 Java编译器知道PriorityQueue构造函数的参数是Comparator,并且知道根据lambda表达式实例化Comparator。在这种情况下,lambda表达式采用两个参数abComparator接口的方法带有两个参数-compare (a,b)。因此,存在可以用于代码生成的匹配项。

因此Java生成了用Comparator的定义替换lamba表达式的代码,该定义的compare (a,b)方法是lambda函数的实现(即{{1右边的文本}}。

我认为,这可能造成混淆的原因是->接口本身是完全不可见的-您需要知道Comparator有一个PriorityQueue参数,而{ 1}}是一个功能接口,它具有一个带有两个参数Comparator的方法,该方法与lamba表达式匹配。