Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/364.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 优先队列,具有保持计数排序的索引_Java_Sorting_Heap_Counting - Fatal编程技术网

Java 优先队列,具有保持计数排序的索引

Java 优先队列,具有保持计数排序的索引,java,sorting,heap,counting,Java,Sorting,Heap,Counting,我在Java中经常遇到的一个问题(通常是在编写计算语言学代码时)是需要计算数据集中某些项的出现次数,然后根据它们的计数对这些项进行排序。最简单的具体示例是单词计数:我需要计算文本文件中每个单词出现的次数,然后按计数对单词进行排序,以找到最常用的单词 不幸的是,Java似乎没有适合此任务的良好数据结构。当我计数时,我需要使用单词作为集合的索引,这样我就可以有效地查找正确的计数器,以便每次读取单词时递增,但是我想要排序的值是计数,而不是单词 Map提供了查找与单词相关联的计数所需的界面,但只能按键对

我在Java中经常遇到的一个问题(通常是在编写计算语言学代码时)是需要计算数据集中某些项的出现次数,然后根据它们的计数对这些项进行排序。最简单的具体示例是单词计数:我需要计算文本文件中每个单词出现的次数,然后按计数对单词进行排序,以找到最常用的单词

不幸的是,Java似乎没有适合此任务的良好数据结构。当我计数时,我需要使用单词作为集合的索引,这样我就可以有效地查找正确的计数器,以便每次读取单词时递增,但是我想要排序的值是计数,而不是单词

Map
提供了查找与单词相关联的计数所需的界面,但只能按键对贴图进行排序(即
TreeMap
PriorityQueue
是一个很好的堆实现,它将根据您提供的任何比较器进行排序,但它无法通过某种索引访问元素,也无法更新和重新堆化元素(除了删除和添加元素)。它的单一类型参数还意味着我需要将单词及其计数粘在一个对象中才能使用它

我目前的“解决方案”是在计数时将计数存储在地图中,然后将它们全部复制到
PriorityQueue
中进行排序:

Map<String, Integer> wordCounts = countStuff();
PriorityQueue<NamedCount> sortedCounts = new PriorityQueue<>(wordCounts.size(),
                                             Collections.reverseOrder());
for(Entry<String, Integer> count : wordCounts.entrySet()) {
    sortedCounts.add(new NamedCount(count.getKey(), count.getValue()));
}
Map wordCounts=countStuff();
PriorityQueue sortedCounts=新的PriorityQueue(wordCounts.size(),
Collections.reverseOrder());
for(条目计数:wordCounts.entrySet()){
添加(新名称count(count.getKey(),count.getValue());
}
(请注意,
NamedCount
只是一个简单的
,它实现了
Comparable
来比较整数)。但这是低效的,特别是因为数据集可能非常大,并且在内存中保留计数集的两个副本是浪费


是否有任何方法可以随机访问
PriorityQueue
中的对象,以便只在PriorityQueue中存储计数的一个副本,并在更新它们时重新加密?如果您可以使用第三方库,如Guava,
Multiset
是专门为解决此问题而设计的,那么使用保持指向
PriorityQueue
中对象的“指针”的
映射是否有意义

Multiset<String> multiset = HashMultiset.create();
for (String word : words) {
  multiset.add(word);
}
System.out.println(Multisets.copyHighestCountFirst(multiset));
Multiset Multiset=HashMultiset.create();
for(字符串字:字){
多集添加(word);
}
System.out.println(multiset.copyHighestCountFirst(multiset));

首先,对于基本数据结构,通常番石榴优于
Map
,正如
Set
优于
Map
一样。它是一个更干净的API,封装了递增的API

现在,如果这是我,我将实现一个定制的
Multiset
,它添加一些额外的逻辑来索引计数,并返回它们。大概是这样的:

class IndexedMultiset<T extends Comparable<T>> extends ForwardingMultiset<T> {

    private final Multiset<T> delegate = HashMultiset.create();
    private final TreeMultimap<Integer, T> countIndex = TreeMultimap.create();

    @Override
    protected Multiset<T> delegate() {
        return delegate;
    }


    @Override
    public int add(T element, int occurrences) {
        int prev = super.add(element, occurrences);
        countIndex.remove(prev, element);
        countIndex.put(count(element), element);
        return prev;
    }

    @Override
    public boolean add(T element) {
        return super.standardAdd(element);
    }

    //similar for remove, setCount, etc


}
public Iterable<CountEntry<T>> descendingCounts() {
    return countIndex.keySet().descendingSet().stream()
            .flatMap((count) -> countIndex.get(count).stream())
            .map((element) -> new CountEntry<>(element, count(element)))
            .collect(Collectors.toList());
}

public static class CountEntry<T> {
    private final T element;
    private final int count;

    public CountEntry(T element, int count) {
        this.element = element;
        this.count = count;
    }

    public T element() {
        return element;
    }

    public int count() {
        return count;
    }

    @Override
    public String toString() {
        return element + ": " + count;
    }
}
public static void main(String... args) {
    IndexedMultiset<String> wordCounts = new IndexedMultiset<>();

    wordCounts.add("foo");
    wordCounts.add("bar");
    wordCounts.add("baz");
    wordCounts.add("baz");

    System.out.println(wordCounts.descendingCounts()); //[baz: 2, bar: 1, foo: 1]


    wordCounts.add("foo");
    wordCounts.add("foo");
    wordCounts.add("foo");

    System.out.println(wordCounts.descendingCounts()); //[foo: 4, baz: 2, bar: 1]
}
class IndexedMultiset扩展了ForwardingMultiset{
private final Multiset delegate=HashMultiset.create();
private final TreeMultimap countIndex=TreeMultimap.create();
@凌驾
受保护的多集委托(){
返回代表;
}
@凌驾
公共整数添加(T元素,整数出现次数){
int prev=super.add(元素、事件);
countIndex.remove(上一个,元素);
countIndex.put(计数(元素),元素);
返回上一个;
}
@凌驾
公共布尔加法(T元素){
返回super.standardd(元素);
}
//移除、设置计数等类似
}
然后,我会根据计数添加您需要的任何查询功能。例如,按降序检索单词/计数对的iterable可能如下所示:

class IndexedMultiset<T extends Comparable<T>> extends ForwardingMultiset<T> {

    private final Multiset<T> delegate = HashMultiset.create();
    private final TreeMultimap<Integer, T> countIndex = TreeMultimap.create();

    @Override
    protected Multiset<T> delegate() {
        return delegate;
    }


    @Override
    public int add(T element, int occurrences) {
        int prev = super.add(element, occurrences);
        countIndex.remove(prev, element);
        countIndex.put(count(element), element);
        return prev;
    }

    @Override
    public boolean add(T element) {
        return super.standardAdd(element);
    }

    //similar for remove, setCount, etc


}
public Iterable<CountEntry<T>> descendingCounts() {
    return countIndex.keySet().descendingSet().stream()
            .flatMap((count) -> countIndex.get(count).stream())
            .map((element) -> new CountEntry<>(element, count(element)))
            .collect(Collectors.toList());
}

public static class CountEntry<T> {
    private final T element;
    private final int count;

    public CountEntry(T element, int count) {
        this.element = element;
        this.count = count;
    }

    public T element() {
        return element;
    }

    public int count() {
        return count;
    }

    @Override
    public String toString() {
        return element + ": " + count;
    }
}
public static void main(String... args) {
    IndexedMultiset<String> wordCounts = new IndexedMultiset<>();

    wordCounts.add("foo");
    wordCounts.add("bar");
    wordCounts.add("baz");
    wordCounts.add("baz");

    System.out.println(wordCounts.descendingCounts()); //[baz: 2, bar: 1, foo: 1]


    wordCounts.add("foo");
    wordCounts.add("foo");
    wordCounts.add("foo");

    System.out.println(wordCounts.descendingCounts()); //[foo: 4, baz: 2, bar: 1]
}
public Iterable downingcounts(){
return countIndex.keySet().downingset().stream()
.flatMap((计数)->countIndex.get(计数).stream()
.map((元素)->新CountEntry(元素,计数(元素)))
.collect(Collectors.toList());
}
公共静态类CountEntry{
私有最终T元素;
私人最终整数计数;
公共计数项(T元素,整数计数){
this.element=元素;
this.count=计数;
}
公共T元素(){
返回元素;
}
公共整数计数(){
返回计数;
}
@凌驾
公共字符串toString(){
返回元素+“:”+计数;
}
}
所有这些都是这样使用的:

class IndexedMultiset<T extends Comparable<T>> extends ForwardingMultiset<T> {

    private final Multiset<T> delegate = HashMultiset.create();
    private final TreeMultimap<Integer, T> countIndex = TreeMultimap.create();

    @Override
    protected Multiset<T> delegate() {
        return delegate;
    }


    @Override
    public int add(T element, int occurrences) {
        int prev = super.add(element, occurrences);
        countIndex.remove(prev, element);
        countIndex.put(count(element), element);
        return prev;
    }

    @Override
    public boolean add(T element) {
        return super.standardAdd(element);
    }

    //similar for remove, setCount, etc


}
public Iterable<CountEntry<T>> descendingCounts() {
    return countIndex.keySet().descendingSet().stream()
            .flatMap((count) -> countIndex.get(count).stream())
            .map((element) -> new CountEntry<>(element, count(element)))
            .collect(Collectors.toList());
}

public static class CountEntry<T> {
    private final T element;
    private final int count;

    public CountEntry(T element, int count) {
        this.element = element;
        this.count = count;
    }

    public T element() {
        return element;
    }

    public int count() {
        return count;
    }

    @Override
    public String toString() {
        return element + ": " + count;
    }
}
public static void main(String... args) {
    IndexedMultiset<String> wordCounts = new IndexedMultiset<>();

    wordCounts.add("foo");
    wordCounts.add("bar");
    wordCounts.add("baz");
    wordCounts.add("baz");

    System.out.println(wordCounts.descendingCounts()); //[baz: 2, bar: 1, foo: 1]


    wordCounts.add("foo");
    wordCounts.add("foo");
    wordCounts.add("foo");

    System.out.println(wordCounts.descendingCounts()); //[foo: 4, baz: 2, bar: 1]
}
publicstaticvoidmain(字符串…参数){
IndexedMultiset wordCounts=新的IndexedMultiset();
字数。添加(“foo”);
字数。添加(“条”);
字数。添加(“baz”);
字数。添加(“baz”);
System.out.println(wordCounts.downingcounts());/[baz:2,bar:1,foo:1]
字数。添加(“foo”);
字数。添加(“foo”);
字数。添加(“foo”);
System.out.println(wordCounts.downingcounts());//[foo:4,baz:2,bar:1]
}

如果您使用Java 8,那么
流中就有这样的工具,为什么不立即使用NamedCount和map
map
?通过这种方式,您可以将getValues()作为一个集合进行排序。@laune虽然简单,但仅使用内置Java 7库听起来是一个不错的解决方案。不过,我倾向于同意Mark Peters的观点,
Multiset
是一种概念上更简洁的设计。此代码不会编译,需要将行“Multiset.add(words)”重写为Multiset.add(word)或更好,以便在初始化Multiset时添加所有元素:HashMultiset.create(words);根据
Multiset
的文档,似乎
copyHighestCountFirst
制作了Multiset的整个副本,以便按计数对其进行排序,这与将我的地图复制到PriorityQueue中一样浪费。一个相当便宜的数据结构(如multiple)的副本