问题描述
这里的任务是我得到输入字符串,例如'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
得到的整数来比较其元素。换句话说,在给定两个元素a
和b
的情况下,队列应通过比较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表达式采用两个参数a
和b
。 Comparator
接口的方法带有两个参数-compare (a,b)
。因此,存在可以用于代码生成的匹配项。
因此Java生成了用Comparator
的定义替换lamba表达式的代码,该定义的compare (a,b)
方法是lambda函数的实现(即{{1右边的文本}}。
我认为,这可能造成混淆的原因是->
接口本身是完全不可见的-您需要知道Comparator
有一个PriorityQueue
参数,而{ 1}}是一个功能接口,它具有一个带有两个参数Comparator
的方法,该方法与lamba表达式匹配。