对频繁出现的单词进行排序的流的替代方案

问题描述 投票:0回答:1

所以,我有一个方法,它将字符串列表作为参数并读取它。然后按频率对它们进行排序,如果单词具有相同的频率,则按字母顺序打印。 (事实上也有俄语单词,它们总是位于英语单词下方)。

这是一个良好输出的示例:

лицами-18
Apex-15
azet-15
xder-15
анатолю-15
андреевич-15
батальона-15
hello-13
zello-13
полноте-13

这是我的代码:

public class Words {

public String countWords(List<String> lines) {

    StringBuilder input = new StringBuilder();
    StringBuilder answer = new StringBuilder();

    for (String line : lines){
        if(line.length() > 3){
            if(line.substring(line.length() - 1).matches("[.?!,]+")){
                input.append(line.substring(0,line.length()-1)).append(" ");
            }else{
                input.append(line).append(" ");
            }
        }
    }

    String[] strings = input.toString().split("\\s");

    List<String> list = new ArrayList<>(Arrays.asList(strings));

    Map<String, Integer> unsortMap = new HashMap<>();
    while (list.size() != 0){
        String word = list.get(0);
        int freq = Collections.frequency(list, word);
        if (word.length() >= 4 && freq >= 10){
            unsortMap.put(word.toLowerCase(), freq);
        }

        list.removeAll(Collections.singleton(word));
    }
    //The Stream logic is here
    List<String> sortedEntries = unsortMap.entrySet().stream()
            .sorted(Comparator.comparingLong(Map.Entry<String, Integer>::getValue)
                    .reversed()
                    .thenComparing(Map.Entry::getKey)
            )
            .map(it -> it.getKey() + " - " + it.getValue())
            .collect(Collectors.toList());
    
    //Logic ends here

    for (int i = 0; i < sortedEntries.size(); i++) {
        if(i<sortedEntries.size()-1) {
            answer.append(sortedEntries.get(i)).append("\n");
        }
        else{
            answer.append(sortedEntries.get(i));
        }
    }

    return answer.toString();

 }
}

我的问题:目前代码运行良好,并且给出了成功的结果,但是正如您所看到的,我正在使用流对字符串进行排序。但是,我只是感兴趣是否有其他解决方案可以在不使用流的情况下编写我的代码。更准确地说,是否有其他方法可以在不使用流的情况下按频率然后按字母顺序(如果它们具有相同的频率)对字符串进行排序。

java dictionary sorting collections java-stream
1个回答
1
投票

您可以在流中执行的任何操作都可以在传统 Java 中执行。但使用流通常可以使代码更短、更简单、更易于阅读!

顺便说一句,代码的前半部分可以简单地替换为:

Map < String, AtomicInteger > map = new HashMap <>();
for ( String word : words ) {
    map.putIfAbsent( word , new AtomicInteger( 0 ) );
    map.get( word ).incrementAndGet();
}

代码的后半部分是通过首先按值排序,然后按键排序来报告地图。

问题中讨论了这一挑战,根据值然后键对 HashMap 进行排序?按值对映射进行排序。这些答案中有一些聪明的解决方案,例如Sean这个

但我宁愿让事情变得简单。我会将单词和字数的映射转换为我们自己的自定义类的对象,每个对象将单词和字数作为字段保存。

Java 16+ 带来了

records 功能,使此类自定义类定义变得更加容易。记录是编写类的一种更简洁的方式,其主要目的是透明且不可变地通信数据。编译器隐式创建构造函数、getters、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; } }
运行时:

words = [андреевич, 你好, xder, батальона, лицами, полноте, анатолю, лицами, полноте, полноте, анатолю, анатолю, zello, 你好, лицами, xder, батальона, Apex, xder, андреевич, анатолю, 你好, xder, Apex, xder, андреевич, лицами, zello, полноте, лицами, Apex, батальона, zello、 полноте、 xder、 你好、 azet、 батальона、 zello、 你好、 полноте、 Apex、 полноте、 полноте、 azet、 андреевич、 полноте、 Apex、 анатолю、 你好、 azet、 лицами、анатолю、zello、анатолю、Apex、zello、андреевич、лицами、xder、你好、полноте、zello、Apex、батальона、лицами、你好、azet、Apex、 анатолю、 анатолю、 zello、 полноте、 анатолю、 Apex、 батальона、 андревич、 лицами、 андреевич、 azet、 azet、 лицами、 лицами、 zello、 azet, анатолю, xder, батальона, полноте, лицами, 你好, лицами, xder, xder, лицами, zello, андреевич, батальона, лицами, андреевич,阿泽特、 полноте、 你好、 андреевич、 лицами、 你好、 Apex、 батальона、 你好、 azet、 лицами、 zello、 батальона、 анатолю、 Apex、 azet、 xder、 андреевич、 андреевич、 батальона、 анатолю、 батальона、 Apex、 xder、 azet、 azet、 xder、 azet、 анатолю、 Apex、 батальона、 Apex、 Apex、 лицами、 батальона、 xder, батальона,你好,андреевич,андреевич,azet,zello,андреевич,xder,azet,анатолю,zello]

地图 = {андреевич=15, xder=15, zello=13, батальона=15, azet=15, лицами=18, анатолю=15, 你好=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[字=анатолю, 计数=15], WordAndCount[字=андреевич,计数=15]、WordAndCount[字=батальона、计数=15]、WordAndCount[字=你好、计数=13]、WordAndCount[字=zello、计数=13]、WordAndCount[字=полноте、计数=13]]

© www.soinside.com 2019 - 2024. All rights reserved.