Find element position in a Java TreeMap
我正在使用字符串的treemap
然后我有一个文件集合,并希望在字典定义的向量空间(单词空间)中创建每个文件的表示。
每个文件都应该有一个向量,用以下属性表示它:
- 矢量的大小应与字典的大小相同
- 对于文件中包含的每个单词,矢量在字典中与单词位置相对应的位置应具有1。
- 对于文件中未包含的每个单词,矢量在字典中单词位置对应的位置应具有-1
所以我的想法是使用
在创建这个向量的过程中,我面临的问题是,我需要一种方法来查找单词在字典中的位置,如下所示:
1 2 |
1)Treemap上有没有这种方法?如果没有,你能提供一些代码来帮助我自己实现它吗?
2)treemap上是否有迭代器(按字母顺序排列在键上),我可以从中得到位置?
3)最后我应该使用另一个类来实现字典吗?(如果你认为Treemaps不能满足我的需求)如果是的,哪一个?
事先谢谢。
增加部分:
DasBlinkenLight提出的解决方案看起来很好,但存在复杂性问题(由于将键复制到数组中,所以与字典的维度呈线性关系),并且对每个文件都这样做的想法是不可接受的。
我的问题还有其他的想法吗?
构建树映射后,将其排序的键复制到一个数组中,并使用
编辑:这是将键复制到数组中的方法
1 2 3 4 5 |
另一种解决方案是使用
下面是如何在Java中编码它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import java.util.*; class Test { public static void main(String[] args) { TreeMap<String,String> tm = new TreeMap<String,String>(); tm.put("quick","one"); tm.put("brown","two"); tm.put("fox","three"); tm.put("jumps","four"); tm.put("over","five"); tm.put("the","six"); tm.put("lazy","seven"); tm.put("dog","eight"); for (String s : new String[] { "quick","brown","fox","jumps","over", "the","lazy","dog","before","way_after"} ) { if (tm.containsKey(s)) { // Here is the operation you are looking for. // It does not work for items not in the dictionary. int pos = tm.headMap(s).size(); System.out.println("Key '"+s+"' is at the position"+pos); } else { System.out.println("Key '"+s+"' is not found"); } } } } |
下面是程序生成的输出:
1 2 3 4 5 6 7 8 9 10 | Key 'quick' is at the position 6 Key 'brown' is at the position 0 Key 'fox' is at the position 2 Key 'jumps' is at the position 3 Key 'over' is at the position 5 Key 'the' is at the position 7 Key 'lazy' is at the position 4 Key 'dog' is at the position 1 Key 'before' is not found Key 'way_after' is not found |
我也有同样的问题。所以我用java.util.treemap的源代码编写了indexedtreemap。它实现了我自己的indexednavigablemap:
1 2 3 4 5 | public interface IndexedNavigableMap<K, V> extends NavigableMap<K, V> { K exactKey(int index); Entry<K, V> exactEntry(int index); int keyIndex(K k); } |
实现的基础是更新红黑树中的节点权重。权重是给定节点下的子节点数,加上一个节点本身。例如,当树向左旋转时:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | private void rotateLeft(Entry<K, V> p) { if (p != null) { Entry<K, V> r = p.right; int delta = getWeight(r.left) - getWeight(p.right); p.right = r.left; p.updateWeight(delta); if (r.left != null) { r.left.parent = p; } r.parent = p.parent; if (p.parent == null) { root = r; } else if (p.parent.left == p) { delta = getWeight(r) - getWeight(p.parent.left); p.parent.left = r; p.parent.updateWeight(delta); } else { delta = getWeight(r) - getWeight(p.parent.right); p.parent.right = r; p.parent.updateWeight(delta); } delta = getWeight(p) - getWeight(r.left); r.left = p; r.updateWeight(delta); p.parent = r; } } |
updateWeight只更新到根的权重:
1 2 3 4 5 6 7 8 | void updateWeight(int delta) { weight += delta; Entry<K, V> p = parent; while (p != null) { p.weight += delta; p = p.parent; } } |
当我们需要通过索引找到元素时,这里是使用权重的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public K exactKey(int index) { if (index < 0 || index > size() - 1) { throw new ArrayIndexOutOfBoundsException(); } return getExactKey(root, index); } private K getExactKey(Entry<K, V> e, int index) { if (e.left == null && index == 0) { return e.key; } if (e.left == null && e.right == null) { return e.key; } if (e.left != null && e.left.weight > index) { return getExactKey(e.left, index); } if (e.left != null && e.left.weight == index) { return e.key; } return getExactKey(e.right, index - (e.left == null ? 0 : e.left.weight) - 1); } |
找到一个键的索引也非常方便:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public int keyIndex(K key) { if (key == null) { throw new NullPointerException(); } Entry<K, V> e = getEntry(key); if (e == null) { throw new NullPointerException(); } if (e == root) { return getWeight(e) - getWeight(e.right) - 1;//index to return } int index = 0; int cmp; if (e.left != null) { index += getWeight(e.left); } Entry<K, V> p = e.parent; // split comparator and comparable paths Comparator<? super K> cpr = comparator; if (cpr != null) { while (p != null) { cmp = cpr.compare(key, p.key); if (cmp > 0) { index += getWeight(p.left) + 1; } p = p.parent; } } else { Comparable<? super K> k = (Comparable<? super K>) key; while (p != null) { if (k.compareTo(p.key) > 0) { index += getWeight(p.left) + 1; } p = p.parent; } } return index; } |
我将很快实现indexedtreeset,同时您可以使用indexedtreemap中的密钥集。
更新:现在实现了indexedtreeset。
你可以在https://github.com/geniot/indexed-tree-map上找到这项工作的结果。
我要感谢你们为回答我的问题所付出的努力,他们都是非常有用的,并且从他们每个人身上得到最好的帮助,使我找到了我在我的项目中实际实施的解决方案。
对于我的单个问题,我认为最好的答案是:
2)treemaps上没有定义为@isoliveira sais的迭代器:
1 2 3 4 5 | There's no such implementation in the JDK itself. Although TreeMap iterates in natural key ordering, its internal data structures are all based on trees and not arrays (remember that Maps do not order keys, by definition, in spite of that the very common use case). |
正如我在这篇文章中发现的,回答如何迭代Treemap?在
3)可以使用
使用同一程序的
1)没有这种方法。唯一的解决方案是完全实现它。
正如@保罗所言
1 | Assumes that once getPosition() has been called, the dictionary is not changed. |
解决方案的假设是,一旦创建字典,以后就不会更改:这样,单词的位置将始终相同。
给出这个假设,我找到了一个解决方案,可以用复杂性o(n)构建字典,并且在garantues之后,可以在lookup中获取constat time o(1)中包含的单词的索引。
我把字典定义为这样的一个
- key——>表示字典中所含单词的
String 。 - 值——>创建的类
WordStruct 的Object 。
其中
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class WordStruct { private int DictionaryPosition; // defines the position of word in dictionary once it is alphabetically ordered public WordStruct(){ } public SetWordPosition(int pos){ this.DictionaryPosition = pos; } } |
并且允许我保存任何类型的属性,我喜欢与字典的词条配对。
现在,我用字典迭代我收集的所有文件中包含的所有单词:
1 2 3 4 5 6 7 8 9 10 11 12 13 | THE FOLLOWING IS PSEUDOCODE for(int i = 0; i < number_of_files ; i++){ get_file(i); while (file_contais_words){ dictionary.put( word(j) , new LemmaStruct()); } } |
一旦hashmap以任何顺序被填充,我就使用@dasbinkenlight所指示的过程来一次性地对其进行排序,并且具有复杂性o(n)
1 2 3 4 5 6 7 8 9 |
从现在起,在字典中按词的字母顺序排列索引位置,唯一需要做的就是计算它的变量
因为Word知道你只需要访问它,所以在
再次感谢,祝大家圣诞快乐!!
JDK本身没有这样的实现。虽然
也就是说,您必须做出选择,因为不可能有o(1)个计算时间用于您的比较标准,无论是插入到
ApacheCommons中的
您还可以自己计算索引,方法是减去低于给定键的元素数(在最常见的情况下,这应该比遍历搜索元素的列表更快,因为您没有比较任何内容)。
你有没有想过让你的
这和我下面的其他想法不太一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | Map<String,Integer> dictionary = new TreeMap<String,Integer> (); private void test () { // Construct my dictionary. buildDictionary(); // Make my file data. String [] file1 = new String[] { "1","3","5" }; BitSet fileDetails = getFileDetails(file1, dictionary); printFileDetails("File1", fileDetails); } private void printFileDetails(String fileName, BitSet details) { System.out.println("File:"+fileName); for ( int i = 0; i < details.length(); i++ ) { System.out.print ( details.get(i) ? 1: -1 ); if ( i < details.length() - 1 ) { System.out.print ("," ); } } } private BitSet getFileDetails(String [] file, Map<String, Integer> dictionary ) { BitSet details = new BitSet(); for ( String word : file ) { // The value in the dictionary is the index of the word in the dictionary. details.set(dictionary.get(word)); } return details; } String [] dictionaryWords = new String[] { "1","2","3","4","5" }; private void buildDictionary () { for ( String word : dictionaryWords ) { // Initially make the value 0. We will change that later. dictionary.put(word, 0); } // Make the indexes. int wordNum = 0; for ( String word : dictionary.keySet() ) { dictionary.put(word, wordNum++); } } |
在这里,文件细节的构建包括在
如果你打算用字典
补充
再想一想,如果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | private void test () { // Dictionary Map<PosKey, String> dictionary = new TreeMap<PosKey, String> (); // Fill it with words. String[] dictWords = new String[] { "0","1","2","3","4","5"}; for ( String word : dictWords ) { dictionary.put( new PosKey( dictionary, word ), word ); } // File String[] fileWords = new String[] { "0","2","3","5"}; int[] file = new int[dictionary.size()]; // Initially all -1. for ( int i = 0; i < file.length; i++ ) { file[i] = -1; } // Temp file words set. Set fileSet = new HashSet( Arrays.asList( fileWords ) ); for ( PosKey key : dictionary.keySet() ) { if ( fileSet.contains( key.getKey() ) ) { file[key.getPosiion()] = 1; } } // Print out. System.out.println( Arrays.toString( file ) ); // Prints: [1, -1, 1, 1, -1, 1] } class PosKey implements Comparable { final String key; // Initially -1 int position = -1; // The map I am keying on. Map<PosKey, ?> map; public PosKey ( Map<PosKey, ?> map, String word ) { this.key = word; this.map = map; } public int getPosiion () { if ( position == -1 ) { // First access to the key. int pos = 0; // Calculate all positions in one loop. for ( PosKey k : map.keySet() ) { k.position = pos++; } } return position; } public String getKey () { return key; } public int compareTo ( Object it ) { return key.compareTo( ( ( PosKey )it ).key ); } public int hashCode () { return key.hashCode(); } } |
注:假设调用
我同意伊索维埃拉的观点。也许最好的方法是使用与treemap不同的结构。
但是,如果您仍然想继续计算键的索引,一个解决方案是计算出有多少键低于您要查找的键。
以下是代码段:
1 2 3 4 5 6 7 8 9 10 11 | java.util.SortedMap<String, String> treeMap = new java.util.TreeMap<String, String>(); treeMap.put("d","content 4"); treeMap.put("b","content 2"); treeMap.put("c","content 3"); treeMap.put("a","content 1"); String key ="d"; // key to get the index for System.out.println( treeMap.keySet() ); final String firstKey = treeMap.firstKey(); // assuming treeMap structure doesn't change in the mean time System.out.format("Index of %s is %d %n", key, treeMap.subMap(firstKey, key).size() ); |
我建议您编写一个skiplist来存储您的字典,因为它仍然提供O(log n)查找、插入和删除,同时还能够提供索引(由于节点不知道索引,所以树实现通常不能返回索引,并且需要花费一定的成本来更新它们)。不幸的是,CONTRONTSKILIPSIMAP的Java实现不提供索引,因此您需要实现自己的版本。
获取一个项目的索引将是O(log n),如果您不进行2次查找就同时需要索引和值,那么您将需要返回一个包含这两者的包装器对象。