您可以在流中执行的任何操作都可以在传统 Java 中执行。但是使用流通常会使代码更短、更简单、更易于阅读!
顺便说一句,您的代码的前半部分可以简单地替换为:
Map < String, AtomicInteger > map = new HashMap <>();
for ( String word : words ) {
map.putIfAbsent( word , new AtomicInteger( 0 ) );
map.get( word ).incrementAndGet();
}
您的代码的后半部分通过首先按值排序,然后按键排序来报告地图。
问题Sorting a HashMap based on Value then Key? 和Sort a Map<Key, Value> by values 中讨论了该挑战。这些答案中有一些巧妙的解决方案,例如this one by Sean。
但我宁愿保持简单。我会将我们的单词和单词计数映射转换为我们自己的自定义类的对象,每个对象都将单词和单词计数作为字段。
Java 16+ 带来了records 特性,使得这样的自定义类定义变得更加容易。记录是编写类的一种更简洁的方式,其主要目的是透明且不可变地传递数据。编译器隐式创建构造函数、getter、equals & hashCode 和 toString。
record WordAndCount (String word , int count ) {}
在 Java 16 之前,使用常规类代替 record。这是该记录单行的 33 行源代码等效项。
final class WordAndCount {
private final String word;
private final int count;
WordAndCount ( String word , int count ) {
this.word = word;
this.count = count;
}
public String word () { return word; }
public int count () { return count; }
@Override
public boolean equals ( Object obj ) {
if ( obj == this ) return true;
if ( obj == null || obj.getClass() != this.getClass() ) return false;
var that = ( WordAndCount ) obj;
return Objects.equals( this.word , that.word ) && this.count == that.count;
}
@Override
public int hashCode () {
return Objects.hash( word , count );
}
@Override
public String toString () {
return "WordAndCount[" + "word=" + word + ", " + "count=" + count + ']';
}
}
我们创建一个该记录类型的对象数组,然后填充。
List<WordAndCount> wordAndCounts = new ArrayList <>(map.size()) ;
for ( String word : map.keySet() ) {
wordAndCounts.add( new WordAndCount( word, map.get( word ).get() ) );
}
现在排序。 Comparator 接口有一些方便的工厂方法,我们可以在其中传递方法引用。
wordAndCounts.sort(
Comparator
.comparingInt( WordAndCount ::count )
.reversed()
.thenComparing( WordAndCount ::word )
);
让我们将所有代码放在一起。
package work.basil.text;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
public class EngRus {
public static void main ( String[] args ) {
// Populate input data.
List < String > words = EngRus.generateText(); // Recreate the original data seen in the Question.
System.out.println( "words = " + words );
// Count words in the input list.
Map < String, AtomicInteger > map = new HashMap <>();
for ( String word : words ) {
map.putIfAbsent( word , new AtomicInteger( 0 ) );
map.get( word ).incrementAndGet();
}
System.out.println( "map = " + map );
// Report on word count, sorting first by word-count numerically and then by word alphabetically.
record WordAndCount( String word , int count ) { }
List < WordAndCount > wordAndCounts = new ArrayList <>( map.size() );
for ( String word : map.keySet() ) {
wordAndCounts.add( new WordAndCount( word , map.get( word ).get() ) );
}
wordAndCounts.sort( Comparator.comparingInt( WordAndCount :: count ).reversed().thenComparing( WordAndCount :: word ) );
System.out.println( "wordAndCounts = " + wordAndCounts );
}
public static List < String > generateText () {
String input = """
лицами-18
Apex-15
azet-15
xder-15
анатолю-15
андреевич-15
батальона-15
hello-13
zello-13
полноте-13
""";
List < String > words = new ArrayList <>();
input.lines().forEach( line -> {
String[] parts = line.split( "-" );
for ( int i = 0 ; i < Integer.parseInt( parts[ 1 ] ) ; i++ ) {
words.add( parts[ 0 ] );
}
} );
Collections.shuffle( words );
return words;
}
}
运行时:
词语= [андреевич,你好,xder,батальона,лицами,полноте,анатолю,лицами,полноте,полноте,анатолю,анатолю,zello,你好,лицами,xder,батальона,顶点,xder,андреевич,анатолю,你好, xder,耳尖,xder,андреевич,лицами,zello,полноте,лицами,耳尖,батальона,zello,полноте,xder,你好,azet,батальона,zello,你好,полноте,耳尖,полноте,полноте,azet,андреевич,полноте, Apex,Анатолю,你好,Zello,Анатолю,alнатолю,alнo,андреевич,лицами,xder,您好,полноте,zello,apex,батальона,лицами,您好,亚洲琴,顶点,Анатолю,Анатолю,Zello, полноте,анатолю,耳尖,батальона,андреевич,лицами,андреевич,azet,azet,лицами,лицами,zello,azet,анатолю,xder,батальона,полноте,лицами,你好,лицами,xder,xder,лицами,zello,андреевич, батальона,лицами,андреевич,azet,полноте,你好,андреевич,лицами,你好,耳尖,батальона,你好,azet,лицами,zello,батальона,анатолю,耳尖,azet,xder,андреевич,андреевич,батальона,анатолю,батальона,一种pex, xder, azet, azet, xder, azet, анатолю, Apex, батальона, Apex, Apex, лицами, батальона, xder, батальона, 你好, андреевич, azet, zello, андрееваи
map = {андреевич=15, xder=15, zello=13, батальона=15, azet=15, лицами=18, анатолю=15, hello=13, Apex=15, полноте=13}
wordAndCounts = [WordAndCount[word=лицами, count=18], WordAndCount[word=Apex, count=15], WordAndCount[word=azet, count=15], WordAndCount[word=xder, count=15], WordAndCount[word=анатолю, count=15], WordAndCount[word=андреевич, count=15], WordAndCount[word=батальона, count=15], WordAndCount[word=hello, count=13], WordAndCount[word=zello, count=13], WordAndCount[word=полноте, count=13]]