匿名用户
1级
2012-03-08 回答
首先要确定你的地域信息是怎么判定的,需要IP的哪些位?
IP分为4段,每段3位。从头到尾,不足不零。
可以形成。最大不超过12位的IP整数。要追求速度,首先需要将12位完整的IP中的某一部分脱离开。现在只需要除开IP段中的某一个段。即能取到9位的整数。这时,java中int类型支持的数字大小是20亿,即10位数,那仅取三段的IP满足int【Integer】的条件。当然,如果像LZ说的,取startIP和endIP,是否就是只二段?这样也可。我说的三段,是需要舍去一段。
现在将刚才我们得到的int型做为Map的key。
为什么要用Integer?下面分析。
首先查询最快的,肯定是HashMap。这里不得不说下HashMap的原理。
1、HashMap里添加一个元素。hashMap.put(key,value);是取Key值的hashCode,经过HashMap的hash(int)的运算,直接得到在HashMap中键数组中应该位于的下标。再将Key和Value的Entry【含Key,Value】放到HashMap里。注意。。是没有明显的遍历操作的。
2、从HashMap中取值,是怎么做的呢?同样,hashMap.get(key)是直接由key值的hashCode得key在键数组中的下标,再取出对应Entry【含Key,Value】。。同样。。没有明显的遍历操作的。
上面2步可以统称为:HashMap的hash算法。具体的实现。你可以去看jdk的源码。
现在就可以回到最开始的,为什么要用Integer类型做key,因为。。Integer重写了hashCode方法。他是直接返回Integer的value字段的,而Integer的eqauls方法甚至直接用的==操作符,这两点决定了高效性,。而String的eqauls和hashCode也重写了,但运算量远大于Integer的。对于HashMap来说,hashCode()和equals()方法,是取值,添加值都会用的。以下会把相关JDK代码贴出来。------如果实在不能用Integer,建议用Long。long的equals用的也是==,hashCode只是对value值进行了无符号右移32位再与原value值取“异或运算”。return (int)(value ^ (value >>> 32));
为什么不用TreeMap呢。我分析了TreeMap的实现。他是这样做的。
1、TreeMap里添加元素。put(key,value),是首先,TreeMap,需要一个对Key的比较器,因为TreeMap是有序的,他的添加是由Key,先找到Key在键数组的位置,再将key,value的Entry放到对应位置。同时设置Entry的前一个和后一个Entry。形成有序Map。在查找Key的位置时,用的是树查找【二叉查找】,从根节点,依次查找。
2、TreeMap里取元素:同样的。用二叉查询方法,找到Key对应的Entry。从而得到Key,Value值。
我做了实验。分别在
1、HashMap里添加1000000条Integer键,String值的随机元素。用时,2500左右毫秒,然后再循环查询10000条随机数据,用时70毫秒左右。
2、TreeMap里做相同的操作,耗时分别为:2800毫秒和95毫秒。
可以认证上述观点。
综上所述。你应该用HashMap做为容器,用Integer做为键。能达到最快查询速度。
下面贴出相关代码。是在JDK1.6的源码里贴出来的。有兴趣的话,可以看一下。
HashMap:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
static int indexFor(int h, int length) {
return h & (length-1);
}
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
TreeMap:
public V put(K key, V value) {
Entry t = root;
if (t == null) {
// TBD:
// 5045147: (coll) Adding null to an empty TreeSet should
// throw NullPointerException
//
// compare(key, key); // type check
root = new Entry(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry parent;
// split comparator and comparable paths
Comparator super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
Comparable super K> k = (Comparable super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry e = new Entry(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
public V get(Object key) {
Entry p = getEntry(key);
return (p==null ? null : p.value);
}
final Entry getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
Comparable super K> k = (Comparable super K>) key;
Entry p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
Integer 的hashCode 和 equals方法:
public int hashCode() {
return value;
}
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
String:的hashCode 和 equals方法:
public int hashCode() {
int h = hash;
int len = count;
if (h == 0 && len > 0) {
int off = offset;
char val[] = value;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
追问:
您这个好像只是 HashMap和TreeMap的代码而已 而且我已经说明了 IP地址已经转变为Long类型了 根本不需要 拆分段之类的了
追答:
请问什么算法能很快的查询到?
我是要用TreeMap 还是HashMap 、数组 存放这个数据库的信息呢? 拿什么当key?
用HashMap,查询数据,直接调用get(key)就行了,不涉及到算法,JDK已经封装好了。我只是从实现上给你分析了。你应该选择HashMap。比TreeMap,数组那些快很多。,hash算法的查询是目前,最快的查询算法。你只需要用就行了。至于你说不拆分,就不拆分,我写那些是描述一种最快的底线,就是用基本类型及其封装类型。别用其它类型做Key。
追问:
就是map里存放的是ip地址段 不是准确的地址 我给一个地址要的是比大小 而不可能直接 get(“ip”) 就能得到。 我所求的算法最简单的比如 将startIp放到treeMap的key中 for循环遍历整个map比较大小 找到比要搜索的ip地址小的最大的key(不太会表达有点绕) 然后取得他的值;稍微好点的像二分查找。其他还有什么快速的算法 我都不算太清楚 因为不知道是不是适合 所以想请各位帮忙解答下,争取找到最优的 因为查询是千万次的
追答:
我能不能理解为?先找,如果找到,就返回,找不到,就遍历,这个元素的上一个和下一个在哪,就是找到和他最近的?
如果要找出元素,HashMap是最快的,如果要找到元素最近的一个,那只有TreeMap才有现成的方法。而且效率肯定比自己写的算法,效率要高。
这样,你涉及到一个取舍。
1、如果做为要找的数据,在查询时大多时候都可以找到的情况下,用HashMap。
2、如果要找的数据,在查询时大多时候都找不到的情况下,用TreeMap。
取舍要凭自己的数据量,访问流来决定。其实,用什么,主要是分析所有影响因素。来做评估。任何事,都有利有弊。以下还有种作法,可贡参考。
当然,还有种作法,以空间换时间,就是用内存,换效率。维护两个Map,两个Map中的元素,保持一至。首先在HashMap中找,用map.get(key),找不到,那就在TreeMap中找,map. lowerEntry(K key),或者map.higherEntry(K key).这两个方法可以找到上一个、下一个元素。