Java中按值自动排序的映射

Java中按值自动排序的映射,java,data-structures,collections,associative-array,sorted,Java,Data Structures,Collections,Associative Array,Sorted,我需要一个Java中的自动按值排序映射,以便在添加新的键值对或更新现有键值对的值,甚至删除某些条目时随时对其进行排序 请记住,这张地图将是非常大的(100的数千条,甚至10的百万条) 因此,基本上我在寻找以下功能: 假设我们有一个实现上述功能的类“SortedByValuesMap” 我们有以下代码: SortedByValuesMap<String,Long> sorted_map = new SortedByValuesMap<String, Long>(); sor

我需要一个Java中的自动按值排序映射,以便在添加新的键值对或更新现有键值对的值,甚至删除某些条目时随时对其进行排序

请记住,这张地图将是非常大的(100的数千条,甚至10的百万条)

因此,基本上我在寻找以下功能:

假设我们有一个实现上述功能的类“SortedByValuesMap” 我们有以下代码:

SortedByValuesMap<String,Long> sorted_map = new SortedByValuesMap<String, Long>();
sorted_map.put("apples", 4);
sorted_map.put("oranges", 2);
sorted_map.put("bananas", 1);
sorted_map.put("lemons", 3);
sorted_map.put("bananas", 6);

for (String key : sorted_map.keySet()) {
  System.out.println(key + ":" + sorted_map.get(key));
}
特别是,对我来说,真正重要的是能够通过 任何时候的最小值-使用如下命令:

smallestItem = sorted_map.lastEntry();
这应该给我一个“橙子”条目

编辑:我是一个Java新手,所以请在你的答案中详细说明一下-谢谢


EDIT2:这可能会有帮助:我用它来计算巨大文本文件中的单词(对于那些熟悉的人:特别是n-grams)。所以我需要建立一个地图,其中键是单词,值是这些单词的频率。然而,由于限制(比如RAM),我只想保留X个最频繁的单词——但是你不能事先知道哪些单词将是最频繁的单词。因此,我认为它可能的工作方式(作为近似值)是开始计算单词,当地图达到上限(比如1 mil条目)时,将删除最不频繁的条目,以便始终将地图大小保持在1 mil。

如果长值不同,则使用附加索引或仅使用
TreeMap
TreeMap
如何


您还可以编写一个。

更新:对不起,您不能按值对地图进行排序

您可以使用
SortedMap
实现,如
TreeMap
Comparator
按值定义顺序(而不是默认的按键)

或者,更好的是,您可以使用预定义的比较器值将元素放入一个比较器中。与树映射相比,它应该更快,占用更少的内存。

如果您只需要“min”值,那么只需使用普通映射并随时跟踪“min”值即可

编辑:

所以,如果你真的需要价值排序,你想使用开箱即用的解决方案,你基本上需要2个集合。一个普通映射(如HashMap)和一个SortedSet(如TreeSet>)。您可以通过树集遍历有序元素,并使用HashMap按键查找频率

显然,你可以自己编写一些东西,比如LinkedHashMap,元素可以按键定位,也可以按顺序遍历,但这几乎完全是自定义代码(我怀疑任何特定的代码已经存在,但我可能是错的)。

解决方案:

//Prepare original data
BiMap<String, Integer> biMap = HashBiMap.create();
biMap.put("apples" , 4);
biMap.put("oranges", 2);
biMap.put("bananas", 1);
biMap.put("lemons" , 3);
biMap.put("bananas", 6);

//Create a desc order SortedMap
SortedMap<Integer, String> sortedMap = new TreeMap<Integer, String>(new Comparator<Integer>(){
    @Override public int compare(Integer o1, Integer o2) {
      return o2-o1;
}});

//Put inversed map
sortedMap.putAll(biMap.inverse());
for (Map.Entry<Integer, String> e: sortedMap.entrySet()) {
      System.out.println(e);
}
System.out.println(sortedMap.lastKey()); 
//准备原始数据
BiMap BiMap=HashBiMap.create();
biMap.put(“苹果”,4);
biMap.put(“橙子”,2);
biMap.put(“香蕉”,1);
biMap.put(“柠檬”,3);
biMap.put(“香蕉”,6);
//创建描述订单分拣图
SortedMap SortedMap=新树映射(新比较器(){
@覆盖公共整数比较(整数o1,整数o2){
返回o2-o1;
}});
//放反映射
sortedMap.putAll(biMap.inverse());
对于(Map.Entry e:sortedMap.entrySet()){
系统输出打印ln(e);
}
System.out.println(sortedMap.lastKey());

您可以参考
java.util.LinkedHashMap
的实现。 其基本思想是,使用内部链接列表来存储订单。以下是一些细节:

扩展自HashMap。在HashMap中,每个条目都有一个键和值,这是基本的。可以添加下一个和上一个指针,以按值顺序存储条目。以及获取第一个和最后一个条目的头和尾指针。对于每次修改(添加、删除、更新),您都可以添加自己的代码来更改列表顺序。它只不过是一个线性搜索和指针开关

当然,如果条目太多,添加/更新速度会很慢,因为它是一个链表而不是数组。但只要对列表进行排序,我相信有很多方法可以加快搜索速度

这里是您得到的:当通过键检索条目时,具有与HashMap相同速度的映射。按顺序存储条目的链表

如果此解决方案满足您的要求,我们可以进一步讨论


致jtahlborn: 正如我所说,如果没有任何优化,它肯定是缓慢的。因为我们现在谈论的是性能而不是impl,所以可以做很多事情

一种解决方案是使用树而不是链表,比如红黑树。然后迭代树,而不是迭代映射

关于最小值,它更容易。仅使用成员变量存储最小值,在添加或更新元素时,更新最小值。删除时,在树中搜索最小的(这非常快)


如果树太复杂,也可以使用另一个列表/数组来标记列表中的某些位置。例如,每个元素可能有100个元素。然后在搜索时,只需先搜索职位列表,然后搜索真实列表。此列表也需要维护,在某些修改时间重新计算位置列表是合理的,可能是100次。

保留2个数据结构:

  • 单词词典->计数。只需使用普通的
    HashMap
  • 用于跟踪顺序的“数组”,这样
    list[count]
    就可以保存一组具有该计数的单词

    我写这篇文章的时候,就好像它是一个数组,作为一种符号方便。事实上,您可能不知道出现次数的上限,因此需要一个可调整大小的数据结构。使用
    地图实施。或者,如果使用太多内存,请使用
    ArrayList
    (您必须测试
    count==size()-1
    ,如果是这样,请使用
    add()
    而不是
    set(count+1)

要增加单词(伪代码)的出现次数,请执行以下操作:

国际热核聚变实验堆
//Prepare original data
BiMap<String, Integer> biMap = HashBiMap.create();
biMap.put("apples" , 4);
biMap.put("oranges", 2);
biMap.put("bananas", 1);
biMap.put("lemons" , 3);
biMap.put("bananas", 6);

//Create a desc order SortedMap
SortedMap<Integer, String> sortedMap = new TreeMap<Integer, String>(new Comparator<Integer>(){
    @Override public int compare(Integer o1, Integer o2) {
      return o2-o1;
}});

//Put inversed map
sortedMap.putAll(biMap.inverse());
for (Map.Entry<Integer, String> e: sortedMap.entrySet()) {
      System.out.println(e);
}
System.out.println(sortedMap.lastKey()); 
// assumes data structures are in instance variables dict and arr
public void tally(final String word)
{
    final long count = this.dict.get(word) or 0 if absent;
    this.dict.put(word, count + 1);
    // move word up one place in arr
    this.arr[count].remove(word);   // This is why we use a Set: for fast deletion here.
    this.arr[count + 1].add(word);
}
for(int count = 0; count < arr.size; count++)
    for(final String word : this.arr[count])
        process(word, count);
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

public class MapValueSort {

    /** inner class to do soring of the map **/
    private static class ValueComparer implements Comparator<String> {
        private Map<String, String>  _data = null;
        public ValueComparer (Map<String, String> data){
            super();
            _data = data;
        }

         public int compare(String o1, String o2) {
             String e1 = (String) _data.get(o1);
             String e2 = (String) _data.get(o2);
             return e1.compareTo(e2);
         }
    }

    public static void main(String[] args){

        Map<String, String> unsortedData = new HashMap<String, String>();
        unsortedData.put("2", "DEF");
        unsortedData.put("1", "ABC");
        unsortedData.put("4", "ZXY");
        unsortedData.put("3", "BCD");


        SortedMap<String, String> sortedData = new TreeMap<String, String>(new MapValueSort.ValueComparer(unsortedData));

        printMap(unsortedData);

        sortedData.putAll(unsortedData);
        System.out.println();
        printMap(sortedData);
    }

    private static void printMap(Map<String, String> data) {
        for (Iterator<String> iter = data.keySet().iterator(); iter.hasNext();) {
            String key = (String) iter.next();
            System.out.println("Value/key:"+data.get(key)+"/"+key);
        }
    }

}
Value/key:BCD/3
Value/key:DEF/2
Value/key:ABC/1
Value/key:ZXY/4

Value/key:ABC/1
Value/key:BCD/3
Value/key:DEF/2
Value/key:ZXY/4
import java.util.*;

/**
 * A map where {@link #keySet()} and {@link #entrySet()} return sets ordered
 * with ascending associated values with respect to the the comparator provided
 * at constuction. The order of two or more keys with identical values is not
 * defined.
 * <p>
 * Several contracts of the Map interface are not satisfied by this minimal
 * implementation.
 */
public class ValueSortedMap<K, V> extends HashMap<K, V> {
    protected Map<V, Collection<K>> valueToKeysMap;

    public ValueSortedMap() {
        this((Comparator<? super V>) null);
    }

    public ValueSortedMap(Comparator<? super V> valueComparator) {
        this.valueToKeysMap = new TreeMap<V, Collection<K>>(valueComparator);
    }

    public boolean containsValue(Object o) {
        return valueToKeysMap.containsKey(o);
    }

    public V put(K k, V v) {
        V oldV = null;
        if (containsKey(k)) {
            oldV = get(k);
            valueToKeysMap.get(oldV).remove(k);
        }
        super.put(k, v);
        if (!valueToKeysMap.containsKey(v)) {
            Collection<K> keys = new ArrayList<K>();
            keys.add(k);
            valueToKeysMap.put(v, keys);
        } else {
            valueToKeysMap.get(v).add(k);
        }
        return oldV;
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

    public V remove(Object k) {
        V oldV = null;
        if (containsKey(k)) {
            oldV = get(k);
            super.remove(k);
            valueToKeysMap.get(oldV).remove(k);
        }
        return oldV;
    }

    public void clear() {
        super.clear();
        valueToKeysMap.clear();
    }

    public Set<K> keySet() {
        LinkedHashSet<K> ret = new LinkedHashSet<K>(size());
        for (V v : valueToKeysMap.keySet()) {
            Collection<K> keys = valueToKeysMap.get(v);
            ret.addAll(keys);
        }
        return ret;
    }

    public Set<Map.Entry<K, V>> entrySet() {
        LinkedHashSet<Map.Entry<K, V>> ret = new LinkedHashSet<Map.Entry<K, V>>(size());
        for (Collection<K> keys : valueToKeysMap.values()) {
            for (final K k : keys) {
                final V v = get(k);
                ret.add(new Map.Entry<K,V>() {
                    public K getKey() {
                        return k;
                    }

                    public V getValue() {
                        return v;
                    }

                    public V setValue(V v) {
                        throw new UnsupportedOperationException();
                    }
                });
            }
        }
        return ret;
    }
}