【问题标题】:How to count and sort repeating strings如何对重复的字符串进行计数和排序
【发布时间】:2017-04-07 15:12:12
【问题描述】:

我有一串名字

String str = "A. Walker, L. Gordon, C. Riley, L. Gordon";

我需要统计出现次数并将出现次数从大到小排序。

countung 部分我已经完成了,但我还需要对其进行排序。

String[] array = str.split(", ");        

List asList = Arrays.asList(array);
Set<String> mySet = new HashSet<String>(asList);
for(String s: mySet)
    System.out.println(s + " " +Collections.frequency(asList,s));

输出应该是这样的

L. Gordon 2, A. Walker 1, C. Riley 1

【问题讨论】:

  • 使用LinkedHashSet 而不是HashSet 来保留插入顺序,并在将asList 传递给集合之前对其进行排序。 (注意,使用List&lt;String&gt; asList 而不是List asList)。
  • 对列表进行排序的一种方法是使用java.util.Collections.sort( List<T> list )。如果需要特殊订单,可以通过 Collections 和 List 中的类似方法提供 Comparator
  • 请注意,如果您已经对列表进行了排序,则无需将它们放入集合中...
  • 谷歌:java count duplicates
  • 如果允许使用外部库,Guava 的 TreeMultiset 是完美的 ADT。

标签: java arrays string hashset


【解决方案1】:

你可以这样做:

public class Test {
    static class NameFreq {
        public NameFreq(String name, int freq) {
            this.name = name;
            this.freq = freq;
        }
        String name;
        int freq;
        @Override
        public String toString() {
            return name + " " + freq;
        }
    }
    public static void main(String[] args) throws Exception {
        String str = "A. Walker, L. Gordon, C. Riley, L. Gordon";
        Map<String, NameFreq> map = new HashMap<>();
        String[] array = str.split("\\s*,\\s*");
        for(String name : array) {
            NameFreq nameFreq = map.get(name);
            if( nameFreq==null )
                map.put(name, new NameFreq(name, 1));
            else
                nameFreq.freq++;
        }

        List<NameFreq> list = new ArrayList<>(map.values());
        Collections.sort(list, new Comparator<NameFreq>() {
            @Override
            public int compare(NameFreq o1, NameFreq o2) {
                return Integer.compare(o2.freq, o1.freq);
            }
        });

        System.out.println(list);
        //output: [L. Gordon 2, A. Walker 1, C. Riley 1]
    }
}

【讨论】:

  • new Integer(o2.freq).compareTo(o1.freq) 不必要地装箱 int 值。请改用Integer.compare(o2.freq, o1.freq)
  • 虽然问题没有指定期望的相同数量的名称排序,但通常需要按名称进行二次排序。这可以通过将HashMap 更改为TreeMap 或通过使用辅助比较逻辑增强Comparator 来完成。就性能而言,第二种选择会更好。
  • @Andreas 是对的,Integer.compare(i1, i2) 方法优于 new Integer(i1).compareTo(i2)
【解决方案2】:

您可以使用stream 轻松做到这一点,例如:

String str = "A. Walker, L. Gordon, C. Riley, L. Gordon";
TreeMap<String,Long> data = Arrays.stream(str.split(","))
    .map(s -> s.trim())
    .collect(Collectors.groupingBy(Function.identity(), TreeMap::new, Collectors.counting()));

LinkedHashMap<String,Long> resultMap= data.entrySet().stream()

.sorted(Map.Entry.comparingByValue().reversed()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); System.out.println(resultMap);

如果你想去掉字符串开头和结尾的大括号,可以使用substring,例如:

String result = resultMap.toString();
if(result.length > 2){
   result = result.substring(1, result.length() - 1);
}
System.out.println(result);

【讨论】:

  • groupingBy 的好用处。
  • OP 希望结果按计数降序排列:"将出现次数从大到小排序"
  • @DarshanMehta descendingMap()key(名称)而不是 value(计数)递减。请参阅my answer,了解按计数降序排序,以及正确构建结果字符串(您当前的结果是A. Walker=1, C. Riley=1, L. Gordon=2,而不是请求的L. Gordon 2, A. Walker 1, C. Riley 1)。
【解决方案3】:

首先,创建一个Map,以名称为键,值为该名称的计数。然后按值降序排序,按键(也称为名称)进行二次排序。

您似乎希望将结果作为逗号分隔的字符串,所以最后以这种方式组合结果。

使用 Java 8 Streams,可以在单个方法链中完成:

String str = "A. Walker, L. Gordon, C. Riley, L. Gordon";
String res = Pattern.compile(", *")
                    .splitAsStream(str)
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                    .entrySet()
                    .stream()
                    .sorted(Comparator.<Entry<String, Long>, Long>comparing(Entry::getValue)
                                      .reversed()
                                      .thenComparing(Entry::getKey))
                    .map(e -> e.getKey() + " " + e.getValue())
                    .collect(Collectors.joining(", "));
System.out.println(res); // prints: L. Gordon 2, A. Walker 1, C. Riley 1

注意splitAsStream() 的使用,因此拆分的结果不必存储在中间数组中。

【讨论】:

  • 对于这个简单的问题,代码是不是太复杂了?
【解决方案4】:

使用trie来统计频率,这样可以节省很多空间。并使用堆对它们进行排序。在推入 trie 时,您可以计算不同单词的数量。如果要按升序排序,则创建该大小的堆,最大堆。

【讨论】:

  • 如果你也想要代码,请告诉我。我会写出来发帖。
【解决方案5】:

这是AbacusUtil的简单解决方案

Stream.of(str.split(", "))
      .toMultiset()
      .toMapSortedByOccurrences(Comparators.reverseOrder());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-19
    • 1970-01-01
    • 2020-05-25
    • 1970-01-01
    • 2017-10-07
    • 1970-01-01
    • 2019-02-05
    相关资源
    最近更新 更多