英文:
How does this Lambda expression in Java help in sorting ? help me understand
问题
public String frequencySort(String s) {
HashMap<Character, Integer> map = new HashMap<>();
for (char c : s.toCharArray()) {
map.put(c, map.getOrDefault(c, 0) + 1);
}
PriorityQueue<Character> maxHeap = new PriorityQueue<>((a, b) -> map.get(b) - map.get(a));
maxHeap.addAll(map.keySet());
StringBuilder result = new StringBuilder();
while (!maxHeap.isEmpty()) {
char current = maxHeap.remove();
for (int i = 0; i < map.get(current); i++) {
result.append(current);
}
}
return result.toString();
}
你的代码实现了一个按照字符频率排序输入字符串的功能。我将使用你提供的例子 'tree'
来解释函数的运行过程。
首先,代码会遍历输入字符串 'tree'
,并将每个字符的出现频率记录在 map
中。在此示例中,map
将包含键值对 ('t', 1)
、('r', 1)
和 ('e', 2)
。
然后,代码创建了一个最大堆 maxHeap
,并使用 lambda 表达式作为参数来定义堆的排序规则。这个 lambda 表达式 (a, b) -> map.get(b) - map.get(a)
中的部分 map.get(b) - map.get(a)
是用来比较两个字符的频率的,从而确定它们在堆中的相对位置。频率较高的字符会在堆的顶部,频率较低的字符会在堆的底部。
接下来,将 map
的键集合('t', 'r', 'e'
)添加到 maxHeap
中,此时堆会按照定义的排序规则进行重新排列。
然后,代码使用一个循环从 maxHeap
中取出字符,根据其频率将字符添加到结果的字符串构建器 result
中。
最终,循环结束后,函数将构建器中的字符串返回作为排序后的结果。
注意:代码中存在一个错误,while
循环的条件应该是 !maxHeap.isEmpty()
,而不是 maxHeap.isEmpty()
,这样循环才会正确执行。
英文:
The task here is that i get input string, for example 'tree' and sort it based on frequency, Output should be 'eetr'/'eert'
, since e repeats 2 times and t,r frequency is 1.
i have this code, what is does it traverses the string, puts all characters in string in Hashmap with their frequencies and a priority queue is used for sorting them in descending order, after which the result is taken in a string builder,
but i need some help in understanding the lambda function used in the parameter of priority queue. Here is my function.
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,1),(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);
}
}
}
can someone explain me the flow using example 'tree' in the function
答案1
得分: 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)
来决定哪个元素先出队。而且你希望是 逆序 的升序(也就是降序)排列。
在你的原始代码中,与其使用返回 Comparator<Character>
的 comparing
方法,它使用了一个基本上是 compare
方法实现的 lambda 表达式。如果你阅读该方法的文档,你会看到你应该:
- 如果第一个参数比第二个参数"小",则返回负整数
- 如果参数相等,则返回 0
- 如果第一个参数比第二个参数"大",则返回正整数
请注意,在数学上,如果 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)
,map.get(a) - map.get(b)
将是 0。
现在你应该明白为什么 map.get(b) - map.get(a)
会通过比较应用 map.get
到每个元素得到的整数来获得 逆序 排序。
其他注意事项:
- 使用减法来实现
Comparator
实际上不是一个好主意,因为会发生溢出。更多信息请参见这里。 - 你的代码有两处拼写错误/错误。1. 在结尾处缺少
return result.toString();
语句 2. 循环控制条件应为while(!maxHeap.isEmpty())
。你漏掉了一个!
。
英文:
> but constructor has only 2 parameters
No, look carefully, in new PriorityQueue(...)
, the ...
part contains only one thing - (a, b) -> map.get(b) - map.get(a)
. PriorityQueue<Character>
's constructor expects one argument of type Comparator<Character>
. A Comparator
is an object that is able to compare two objects and tell which is "greater", or whether they are equal. The priority queue will use this Comparator
to order its elements.
If you find the lambda expression confusing, how about rewriting the code to:
new PriorityQueue<>(Comparator.comparing(map::get).reversed());
The intent should be clearer, and it does roughly the same thing. We are creating a priority queue that should compare its elements by comparing the integers we get by applying map.get
on each of them. In other words, given two elements a
and b
, the queue should decide which element comes first by comparing map.get(a)
and map.get(b)
. And you want the reverse ascending (i.e. descending) order.
In your original code, rather than using the comparing
method which returns a Comparator<Character>
, it is using a lambda expression which is basically the implementation of the compare
method. If you read the documentation for that method, you will see that you should:
- return a negative integer if the first argument is "smaller" than the second
- return 0 if the arguments are equal
- return a positive integer if the first argument is "greater" than the second
Observe that mathematically, if map.get(a) < map.get(b)
, map.get(a) - map.get(b)
will be negative. If map.get(a) > map.get(b)
, map.get(a) - map.get(b)
will be positive, and if map.get(a) = map.get(b)
, map.get(a) - map.get(b)
will be 0.
Now you should see why map.get(b) - map.get(a)
gives you the reverse order by comparing the integers got by applying map.get
to each element.
Miscellaneous notes:
-
Using subtraction to implement
Comparator
is not actually a good idea, because overflow. See here for more info. -
Your code has two typos/mistakes. 1. missing a
return result.toString();
statement at the end 2. the loop control condition should bewhile(!maxHeap.isEmpty())
. You missed a!
.
答案2
得分: 1
它与Scala
中的wordCount
示例非常相似。但它在一个字符串中计算单词,而您的程序在一个单词中计算字母。非常相似。
然而,
映射输出将如下所示:
> (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之后排序。如果为零,则它们相等。
请参阅Javadoc PriorityQueue<E>:基于优先堆的无界优先级队列。优先级队列的元素根据其自然顺序进行排序,或者根据在队列构建时提供的比较器进行排序,具体取决于使用哪个构造函数。
英文:
It's very similar to wordCount
example in Scala
. But it counts words in a string but your program counts letters in a word. very similar.
However,
the map output will be as follow:
>(t,1), (r,1), (e,2)
The map.get(b) - map.get(a)
is just used as comparator. If map.get(b) - map.get(a)
be negative which means map.get(b) < map.get(a)
and results to order before a. If be positive means map.get(b) > map.get(a)
and results to order after a. If be zero, they are equal.
See Javadoc for PriorityQueue<E> : An unbounded priority queue based on a priority heap. The elements of the priority queue are ordered according to their natural ordering, or by a Comparator provided at queue construction time, depending on which constructor is used.
答案3
得分: 1
“隐式”比较器的使用可能会非常令人困惑。自从Java 8以来,PriorityQueue
构造函数被定义为接受一个 java.util.Comparator
作为其参数之一。Comparator
接口指定了一个名为 compare()
的函数,它接受两个参数并返回一个整数。整数的符号表示哪个元素较大。这就是为什么在原帖中,两个元素的值被相减 —— 如果它们相等,结果将为零。如果一个比另一个大,则结果将为正或负。PriorityQueue
实现使用这些结果,应用于许多元素,以确定应该放置它们的顺序。
在 lambda 表达式出现之前,这种逻辑我认为相当直观。现在,Comparator
被声明为一个“函数式接口”。这意味着可以直接从 lambda 表达式创建接口的匿名实例化。Java 编译器知道传递给 PriorityQueue
构造函数的参数是一个 Comparator
,它知道要基于 lambda 表达式实例化一个 Comparator
。lambda 表达式在这种情况下接受两个参数 a
和 b
。Comparator
接口有一个接受两个参数的方法 —— compare(a, b)
。因此在代码生成中可以使用这种匹配。
因此,Java 生成的代码将 lambda 表达式替换为一个 Comparator
的定义,其 compare(a, b)
方法是 lambda 函数的实现(即 ->
右侧的文本)。
我认为可能会令人困惑的是,Comparator
接口本身是完全不可见的 —— 你需要知道 PriorityQueue
有一个 Comparator
参数,并且 Comparator
是一个具有两个参数的函数式接口,即 compare()
方法与 lambda 表达式匹配。
英文:
The use of "implicit" comparators like this has, I think, the potential to be very confusing. The PriorityQueue
constructor (since Java 8) is defined to take a java.util.Comparator
for one it arguments. The Comparator
interface specifies a function compare()
that takes two arguments, and returns an integer. The sign of the integer indicates which of the elements is the larger. That's why, in the OP, the values of the two elements are subtracted -- if the are equal, the result will be zero. If one is larger than the other, the result will be either positive or negative. The PriorityQueue
implementation uses these results, applied to many elements, to determine the order they should be placed in.
This logic was, I think, fairly intuitive before lamba expressions came on the scene. Now, Comparator
is declared to be a "functional interface". That means an anonymous instantiation of the interface can be created directly from a lamba expression. the Java compiler knows that the argument to the PriorityQueue
constructor is a Comparator
, and it knows to instantiate a Comparator
based on the lambda expression. The lambda expression takes two arguments, a
and b
in this case. The Comparator
interface has a method that takes two arguments -- compare (a,b)
. Hence there is a match that can be used in code generation.
So Java generates code that replaces the lamba expression with a definition of a Comparator
whose compare (a,b)
method is the implementation of the lambda function (that is, the text to the right of the ->
).
What makes this potentially confusing, I think, is that the Comparator
interface itself is completely invisible -- you need to know that PriorityQueue
has a Comparator
argument, and that Comparator
is a functional interface that has one method with two arguments, compare()
, that matches the lamba expression.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论