List.sort 有两个字段

0 java lambda arraylist

我在leetcode讨论部分看到了这段代码,但我无法理解它。请在这里帮忙。

HashMap<Character,Integer> map = new HashMap<>();
    for(char ch: s.toCharArray()){
        map.put(ch,map.getOrDefault(ch,0)+1);
    }
    List<Character> list = new ArrayList<>(map.keySet());//convert hashmap keys into list
    list.sort((x,y) -> map.get(y) - map.get(x));
Run Code Online (Sandbox Code Playgroud)

我无法理解下面一行的含义。

list.sort((x,y) -> map.get(y) - map.get(x));

Bas*_*que 6

实际上,这短短一行中发生了数量惊人的事情:list.sort((x,y) -> map.get(y) - map.get(x));

\n

List.sort( Comparator comparator )

\n

List.sort方法需要一个Comparator对象作为参数。

\n

@FunctionalInterface

\n

如果您查看该Comparator界面,您\xe2\x80\x99将看到它被注释为@FunctionalInterface. 引用 Javadoc,\xe2\x80\x9c 从概念上讲,函数式接口只有一个抽象方法。\xe2\x80\x9d。

\n

拉姆达

\n

这段代码:

\n
(x,y) -> map.get(y) - map.get(x)\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\xa6 是 lambda 语法,充当该单个抽象方法的实现。

\n

该 lambda 是用紧凑的语法编写的。您可能会发现扩展格式很容易解释。

\n
( String x , String y ) -> { return map.get( y ) - map.get( x ); }\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\xa6 如:

\n
list.sort( ( String x , String y ) -> { return map.get( y ) - map.get( x ); } );\n
Run Code Online (Sandbox Code Playgroud)\n

编译器知道需要一个Comparator对象作为 的参数List#sort。因此, 编译器可以推断这个 lambda 实现的方法应该是 的compare方法Comparator,这是在 上找到的唯一抽象方法Comparator

\n

Comparator#compare

\n

Comparator#compare方法返回一个int整数。引用 Javadoc:返回的数字表示 \xe2\x80\x9ca 负整数、零或正整数,因为第一个参数小于、等于或大于第二个参数。\xe2\x80\x9d。

\n

您的键值对的值侧HashMap属于类型Integer类。因此,仅仅Integer从一个值对象中减去另一个值对象是一种看似聪明的方法,可以确定该数字要返回的整数compare

\n

整数溢出

\n

\xe2\x9a\xa0\xef\xb8\x8f 然而,正如下面评论的,上述方法在数学上是有缺陷的。减法可能会导致整数溢出

\n

Integer.compare

\n

最好将比较工作委托给类中的方法IntegerInteger.compare

\n
( x , y ) -> Integer.compare( x , y ) \n
Run Code Online (Sandbox Code Playgroud)\n

示例代码:

\n
(x,y) -> map.get(y) - map.get(x)\n
Run Code Online (Sandbox Code Playgroud)\n

运行时:

\n
( String x , String y ) -> { return map.get( y ) - map.get( x ); }\n
Run Code Online (Sandbox Code Playgroud)\n

我们看到我们的列表排序为Bob, Carol, Alice,顺序为 (42, 7, 2)。

\n

如果对该方法的实现感到好奇,请检查OpenJDK 项目上的开源代码代码。摘抄:

\n
public static int compare(int x, int y) {\n    return (x < y) ? -1 : ((x == y) ? 0 : 1);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Comparator.comparingInt

\n

另一种方法是使用Comparatorstatic 返回的对象Comparator.comparingInt方法返回的对象。您的 IDE(例如 IntelliJ)甚至可能会建议此代码。

\n
list.sort( Comparator.comparingInt( list :: get ) )\n
Run Code Online (Sandbox Code Playgroud)\n

对静态方法的调用Comparator.comparingInt返回一个Comparator对象。正是我们需要传递给List#sort方法的内容。

\n

map :: get部分是方法参考

\n

例子:

\n
list.sort( ( String x , String y ) -> { return map.get( y ) - map.get( x ); } );\n
Run Code Online (Sandbox Code Playgroud)\n

相反的顺序

\n

您的示例代码实际上按降序而不是升序对数字顺序进行排序

\n

如果您想要这种效果,可以使用类中的另一个方便的方法来实现Comparatorreversed

\n

使用方法链接来追加.reversed,如下所示:Comparator.comparingInt( map :: get ).reversed()

\n
( x , y ) -> Integer.compare( x , y ) \n
Run Code Online (Sandbox Code Playgroud)\n

结果:

\n
// Source of data.\nfinal Map < String, Integer > map =\n        Map.of(\n                "Alice" , 42 ,\n                "Bob" , 2 ,\n                "Carol" , 7\n        );\nSystem.out.println( "map = " + map );  // Be aware that a Map can iterate in any order at any time.\n\n// Unsorted list.\nfinal List < String > list = new ArrayList <>( map.keySet() );  // List may be built from a map in any order, given that a map iterates in no particular order.\nSystem.out.println( "list = " + list );\n\n// Sorted list.\nlist.sort( ( String x , String y ) -> { return Integer.compare( map.get( x ) , map.get( y ) ); } );\nSystem.out.println( "list = " + list );\n
Run Code Online (Sandbox Code Playgroud)\n

我们看到新的结果Alice, Carol, Bob(42, 7, 2) 与之前的结果相反Bob, Carol, Alice(42, 7, 2) 与之前的(2, 7, 42)

\n

自动装箱

\n

此外,该map.get(y) - map.get(x)部分还会调用自动装箱

\n

每次get调用都会返回一个类型为 class 的对象Integer-直接对对象使用减号Integer是没有意义的;物体不能被减去。然而,编译器足够聪明,可以识别Integer对象可以转换为int基元。还有两个int基元可以相减。

\n
\n

代码点

\n

顺便说一下,Characterclass 是原始类型的包装类char。自 Java 2 以来,两者都已本质上被破坏,而自 Java 5 以来,两者都已被破坏。作为 16 位值,achar在物理上无法表示大多数字符。

\n

相反,在处理单个字符时,请学习使用代码点整数。Unicode 中定义的 144,697 个字符中的每一个都被永久分配了一个特定的数字。数字范围从零到略多于一百万。

\n

您将在、、等codePoint\xe2\x80\xa6类上找到方法。StringCharacterStringBuilder

\n

  • 请注意,“x - y”是一种(不幸的是流行的)反模式。它在下溢/溢出情况下产生不正确的结果。更好的实现方法是通过 `Integer.compare(x, y)` 或显式使用 `x &lt; y ? -1 : (x == y ? 0 : 1)`。如果数字很大的话,速度会更快。 (2认同)