新年好:
很久没更新博客了,在这里先检讨一下,最近有关计算机方面的关注变少了。本博客虽然是一个多人博客,但是最近交流的比较少,大家也都比较忙。其实我觉得懒惰才是我们最大的敌人,哈哈。
不说这些了,祝愿在光棍年里光棍早日找到自己的另一半,不是光棍的早日成亲!
更衷心的希望新的一年本博客能迎来更多的作者。
by Alex
新年好:
很久没更新博客了,在这里先检讨一下,最近有关计算机方面的关注变少了。本博客虽然是一个多人博客,但是最近交流的比较少,大家也都比较忙。其实我觉得懒惰才是我们最大的敌人,哈哈。
不说这些了,祝愿在光棍年里光棍早日找到自己的另一半,不是光棍的早日成亲!
更衷心的希望新的一年本博客能迎来更多的作者。
by Alex
版权声明:可以任意转载,但转载时必须标明原作者charlee、原始链接http://tech.idv2.com/2007/09/17/build-dns-server-on-win32/以及本声明。
相信有很多人都想架设自己的DNS服务器。我们知道世界上最好用的DNS服务器软件就是BIND; 但是我辈使用Windows操作系统的人就无福享用这Unix下的顶级软件了。
或者可以用Windows Server自带的DNS服务器试试? 需要安装Server版的Windows不说,麻烦的配置和令人迷惑的图形界面就够受的了。
难道就没有一个解决方案了吗?
柳暗花明又一村,突然发现BIND居然有Windows版,这这这………… 赶快下载下来试一下,居然成功地配好了DNS。
BIND的Windows版叫做ntbind,在isc的ftp上有下载。 我下载的是ntbind-9.2.5版。解压之后运行安装程序,默认安装到C:Windowssystem32dns下。
装好之后就是配置工作了,不过在这之前建议先将 C:Windowssystem32dnsbin 添加到 PATH 环境变量中, 这样配置时就可以用 dig工具来代替难用的 nslookup了。然后再将自己机器的DNS地址改为 127.0.0.1。 注意修改DNS时别忘记ISP提供的DNS地址,过一会儿要用到。
打开 C:Windowssystem32dnsetc 目录,建立配置文件 named.conf,内容如下:
named.conf
options { // zone文件的位置 directory "C:Windowssystem32dnsetc"; // 无法解析的域名就去查询ISP提供的DNS // 在下面的IP地址位置上填写ISP的DNS地址 forwarders { 1.2.3.4; 1.2.3.5; }; // 仅允许本机和子网内的机器查询 allow-query { 127.0.0.1; 192.168.0.0/24; };};// 根DNSzone "." { type hint; file "named.root";};// localhostzone "localhost" IN { type master; file "localhost.zone"; allow-update { none; };};// localhost的反向解析zone "0.0.127.in-addr.arpa" { type master; file "localhost.rev";};// example.comzone "example.com" IN { type master; file "example.com.zone";};# End of named.conf
然后逐个建立named.conf中提到的几个文件,都放在 C:Windowssystem32dnsetc 下。
named.root:可以从ftp.rs.internic.net(匿名FTP)上下载。
localhost.zone:针对localhost的正向解析。
$TTL 1D@ IN SOA localhost. root.localhost. ( 2007091701 ; Serial 30800 ; Refresh 7200 ; Retry 604800 ; Expire 300 ) ; Minimum IN NS localhost.localhost. IN A 127.0.0.1
localhost.rev:针对127.0.0.1的反向解析。
$TTL 1D@ IN SOA localhost. root.localhost. ( 2007091701 ; Serial 30800 ; Refresh 7200 ; Retry 604800 ; Expire 300 ) ; Minimum IN NS localhost.1 IN PTR localhost.example.com.zone:是我们为自己的域的正向解析配置。
example.com. IN SOA ns1.example.com. root.example.com. ( 2007091701 ; Serial 30800 ; Refresh 7200 ; Retry 604800 ; Expire 300 ) ; Minimum IN NS ns1.example.com.* IN A 192.168.0.2 ; 将所有域名都泛解析到192.168.0.2上OK,这几个配置文件写好之后,启动命令行,输入以下命令:C:> named -f -g -d 1即可在控制台启动named。如果不能启动请仔细观察输入结果并自行查找错误。
然后你可以用dig命令来测试返回结果是否正确。
C:> dig www.google.comC:> dig www.sina.com.cn
你也可以打开浏览器,看看能否正常上网。另外因为我们配置了 example.com 的域, 所以 abc.example.com 应该能访问你架设在 192.168.0.2 上的 Web 服务器。
一切正常访问之后,我们还有一件事情要做:配置使用 rndc 命令来控制bind。 请执行以下命令:
C:> cd C:Windowssystem32dnsetcC:Windowssystem32dnsetc> rndc-confgen > rndc.conf
即可在 C:Windowssystem32dnsetc 下生成 rndc.conf 文件。编辑这个文件, 并将该文件的后半部分剪切到 named.conf 末尾,配置即完成。
重启 named,然后在命令行输入 rndc reload,应该能在named的控制台看到 重新加载配置文件的信息,说明配置成功。
最后一步,利用srvany将named安装为服务,即大功告成。(srvany需要安装Windows 2003 Server Resource Kit)
instsrv ntbind C:Windowssystem32dnsbinnamed.exe
参考文献
2010-11-05 22:36
数字大战字母,360 VS QQ。从2010年10月3号18点开始,这个话题风靡了整个中国局域网,风头盖过了美国中期大选、他爸李刚、50种蔬菜价格上涨等重大事件。这表明了QQ对中国局域网的巨大影响力。
QQ使用这种方式强迫用户卸载360,是不是太过自负了呢?会使QQ走向衰落甚至灭亡吗?反正我周围的人都认为马化腾脑袋发热了。但是,从股价上来看,从11.03的收盘价187.00到11.05的收盘价183.00,似乎跌幅并不大,这一收盘价更是高于10天均价1882.03、30天均价178.99、50天均价167.68。这证明了资本市场对此事的反应并不激烈。
但这件事无疑是中国局域网发展历史上的一件大事,十年后甚至二十年后都会有人重新提起这件事。我认为它的意义主要在于两点:
其一:使更多的初级用户了解到QQ扫描硬盘,侵害隐私的本质。虽然这一点在技术群体中已是众人皆知,但在广泛的初级用户群体中并没有流传开来。这件事情之后,大多数用户都会或多或少的了解到这一点。而且由于腾讯在公关方面的失误,使这一影响更为严重:对于360的舆论打压是很没有技术含量的,只是堆砌大量的技术词汇进行描述最终得出结论,并没有明确简单的指出360的后门及危害,而360更是以方便用户操作为特点,把技术问题用简单的描述告诉用户,使用者大部分又是初级用户,对计算机知识了解不多,而腾讯用大量技术词汇向这一群体宣传,结果不证自明;在腾讯微博上对于有关360信息的屏蔽更是使人感觉此地无银三百两;每次已公司名义发表的声明都避开隐私问题不谈,更是心虚的表现。
其二:使很多用户产生对桌面软件的不信任感。金山傲游可牛搜狗百度无疑是这一过程的推手。用户,尤其是初级用户,对于计算机的操作本身就不熟练,而这些厂商又人为地制造使用障碍,这不是把用户往Web应用上推吗。本来就是个客户端软件岌岌可危的时代,这些厂商之间又在不断地进行恶性斗争,人为地给用户制造麻烦。在这个背景下,只要有公司能够提供稳定地Web应用,客户端的衰落必然会加剧。相关的Web应用举例:Google Reader、twitter官方版、docs.google.com、webqq2.0、腾讯财经、腾讯阅读、Google地图。。。太多了。
所以,我认为以后网页版的应用会流行起来,而这一事件更是加速了这一天的到来。但腾讯会不会衰落,我和资本市场的观点是一致的。腾讯现在已经不是QQ了,它拥有一个相互支撑的帝国,而且对于业界的把握也很到位。
中国局域网就是一场大戏,期待360的反制措施,哈哈。
如果你用ultraiso写入iso文件到U盘后,开机就显示两行,第一行是starting from usb。。。 第二行有 syslinux 。。。但是就是启动不了。可以用这种方法试试:用ultraiso写入U盘后,将syslinuxsyslinux.cfg中的default vesamenu.c32用#号注释掉就可以从U盘启动了。
这不是一篇技术类博客,只是一篇有关跑步和健身的废话,没兴趣的童鞋请绕行。
----------------------------------------------------------------
我从小就体弱,小学的时候甚至有不能从学校走到家的情况,就待在路边等家里人来接我。上学的时候也一直没有好好锻炼身体,记得大学时上体育课跑步跑两圈都气喘吁吁的。
第一次比较系统的锻炼跑步就是在2008年上半年复习考研的时候,因为当时已经大四,就算正常考上研究僧也已经比别人晚了一年,压力比较大。几乎每天都在自习室里度过,学习的时间长了就会不舒服。要么是中午吃完饭叫上李亮一块在大太阳下跑3-4圈,要么是晚上8点左右自己去学校的小操场(煤渣地)上跑4-6圈。大概坚持了2个多月就到毕业的时候,每天饭醉,自习室去的都比以往少了,更枉伦跑步了。
毕业之后,回家呆了一个多月就到北京继续复习考研,当时是在北外,各种条件都不行,连个自习室都没有,都是去食堂仔细。相信现在去北外食堂也会看到不少人在自习吧。也没有继续锻炼。考研后,回家过年,考研成绩出来之后来北京呆了两个月,期间完成了复试,去了趟山西,回北京呆了阵。实在没事就回家了,又去昆明找我表弟,直到开学。这期间都怎么锻炼过。
研究僧开学以后,逐渐开始在晚上去操场跑步,刚开始是4圈,后来渐渐增加到6圈。大概又跑了两个多月,天气转冷,我和一个一起和我跑步的哥们一块得了重感冒,就没有继续了。
再后来就是寒假回家过春节,到开春开学的时候,去学校体育馆办了张健身卡,100元3个月。用跑步机跑,刚开始不怎么会跑,速度调为10千米每小时,跑10-11分钟,每次膝盖都痛。不过时间长了,慢慢地找到了方法,也开始逐渐增加运动量,刚开始时是10KM/H跑10-11分钟,逐渐增加为15分钟,速度也逐渐提高。这期间也增加了锻炼力量的项目,刚开始是随便练习一些器械,也不讲什么标准,拉得动就算。后来体力越来越好,锻炼的器械也越来越明确。
直到2010年8月份,几乎没有间断的锻炼了5个月以后,锻炼的各种项目基本都已经确定,一般都是50*3个仰卧起坐,12*3个引体向上(有50磅的助力),12*3个20磅哑铃弯举 + 15分钟的12千米每小时的跑步。
最近学校在搞校庆,健身馆给封了,很是郁闷。只好去操场跑步,一般都是跑10圈还有余力。甚感欣慰。
最后想想,人真是一种神奇的生物,本来跑两圈都累得不行,通过坚持做重复的事情,竟然能提高这么多。
2010-09-26写于实验室,希望一年后的自己不要吓得不敢看。
最近一直在看《1984》,它的作者是乔治.奥威尔 George Orwell。
值得一看。
==================================================================
最近想做一个团购导航网站,大家都很有热情,
Frederick Jelinek是信息论,自动语音识别,和自然语言处理领域的大师。他于2010年9月14日逝世。
第一次知道大师的名字是在Google研究员吴军所写的数学之美系列文章中,甚是仰慕。
详情请参见:http://www.clsp.jhu.edu/
http://en.wikipedia.org/wiki/Frederick_Jelinek
愿老人家一路走好!
用过 Mac 或者 Linux 的童鞋可能都体验过“虚拟桌面”的功能。如果体验过,并想在 Windows 下使用该功能,这款软件是您的不二选择,直接跳到文章底部下载吧!
如果您没用过,没关系,但这种情况您肯定遇到过:开了两个窗口写文章,又要上网查资料,同时还在听歌,还有几个人和你聊天。
是不是很混乱,要不断的切换窗口,最大化最小化。。。
有了Dexpot就不用愁了,它可以给您虚拟出多个桌面:您可以单独用一个窗口写文章和查资料;第二个窗口听歌;第三个窗口专门聊天。然后在不同的窗口干不同的事情而不互相干扰。
基本上就是这样,附上异次元软件世界的推荐与小众软件的推荐。
官方网站(德语):http://dexpot.de/index.php?id=home
最新版下载地址:http://dexpot.de/download/dexpot_155_r1302.exe
绿色版下载地址:http://www.xdowns.com/soft/softdown.asp?softid=16269
"逃出川小周" 我想这段话大家应该都见过吧,今天特意做了一个Google拼音的扩展,用于自动反转你所输入的文字。
将以下代码保存为.lua文件,并到Google拼音中安装即可。这时候在候选项转换器中就会有:”反转您要说的话“了。
-- encoding: UTF-8
function ReverseInput(text)
local ret = ""
local i = #text
while i >= 1 do
if text:byte(i, i) < 128 then
ret = ret .. text:sub(i, i)
i = i - 1
else
ret = ret .. text:sub(i - 2, i)
i = i - 3
end
end
return ret
end
------------
ime.register_converter("ReverseInput", "反转您要说的话")
简介
packagejava.util;
importjava.io.*;
publicclass Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable,java.io.Serializable
Hashtable属于包java.util,实现中使用到了包java.io中的某些类。
它继承自Dictionary类,实现了Map接口,Cloneable接口,Serializable接口
Dictionary没有继承任何类(publicabstract class Dictionary<K,V>);
变量
privatetransient Entry[] table;
privatetransient int count;
privateint threshold;
privatefloat loadFactor;
privatetransient int modCount = 0;
privatestatic final long serialVersionUID = 1421746759512286392L;
table用于实际的存储key与value对.
默认值 & 构建方法
放在了构建方法里,没用声明静态常量;这一点就没有HashMap好。
由以下的构建方法可知,默认大小为11,装载因子为0.75;
和HashMap一样有四种构建方法;
public Hashtable() {
this(11, 0.75f);
}
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
public Hashtable(int initialCapacity, floatloadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw newIllegalArgumentException("Illegal Load: "+loadFactor);
if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];
threshold = (int)(initialCapacity *loadFactor);
}
public Hashtable(Map<? extends K, ?extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
同步 & 线程安全:
Hashtable里的很多方法都是synchronized的,所以它是线程安全的。
Hash值与索引的实现:
以put为例:
publicsynchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already inthe hashtable.
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) %tab.length;
for (Entry<K,V> e = tab[index] ; e!= null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}
modCount++;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
index = (hash & 0x7FFFFFFF) %tab.length;
}
--------------------end-----------------------
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) %tab.length;
hash值就是利用这两行代码来实现的,只是取了hash数的后31位并对数组长度求余.
比HashMap的实现还粗糙.
Hash冲突的处理:
和HashMap类似,利用一种拉链法的方式来处理,HashMap的实现原理:
“计算hash值=> 找hash值在table中对应的索引=> 判断该索引是否为null:
为null,直接添加;
不为null,判断hash值和key值是否相同(即使不同的hash值得到的索引i也可能相同),相同直接更新V,不相同则继续e = e.next();直到找到具有完全相同hash值和key值的e(Entry),或e == null;
如果最终e == null; 而且未找到完全相同的Entry,则运行addEntry(hash, key, value, i);
void addEntry(int hash, K key, V value,int bucketIndex) {
Entry<K,V> e =table[bucketIndex];
table[bucketIndex] = newEntry<K,V>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length); //增长方式:*2;
}
利用一个单链的方式处理hash冲突:
假如本来table[i]为A,且A.next() == null;
这时候put方法获取了一个hash值与A的相同,但key值与A的不同的参数,则利用获得的两个参数key和value以及hash值与索引i值组合生成一个Entry B, 则将B放入table[i],将B.next 指向A;
同理,put了一个C,则将C放入table[i],C.next指向B;”
只是这里的实现略有不同,它并不是将添加新的Entry封装为一个方法,而是直接生成一个Entry对象,在put()代码中去实现添加。
Null值的处理:
Hashtable中不允许任何形式的null值!
Value为null时:
put()中的
if (value == null) {
throw new NullPointerException();
}
Key为null时;
因为无论是在put()还是get()里;
都是没有进行判断key是否为null而直接使用
int hash = key.hashCode();
所以当key为null时就会抛出异常:
Exception in thread "main"java.lang.NullPointerException
所以说Hashtable不允许任何形式的null值。
增长方式:
方法为rehash(),该方法只会在put()方法中被直接调用到:
protected void rehash() {
int oldCapacity = table.length;
Entry[] oldMap = table;
int newCapacity = oldCapacity * 2 + 1;
Entry[] newMap = new Entry[newCapacity];
modCount++;
threshold = (int)(newCapacity *loadFactor);
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;){
for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
int index = (e.hash &0x7FFFFFFF) % newCapacity;
e.next = newMap[index];
newMap[index] = e;
}
}
}
由这行代码:int newCapacity = oldCapacity * 2 + 1;
可知Hashtable的增长方式为 *2 + 1;
遍历方式:
遍历Entry, Set<Map.Entry<K,V>>;
public Set<Map.Entry<K,V>>entrySet();
遍历value, Enumeration<V> || Collection<V>
public synchronized Enumeration<V>elements(); //与HashMap不同
public Collection<V>values();
遍历key, Enumeration<K> || Set<K>;
public synchronized Enumeration<K>keys(); //与HashMap的实现不同
public Set<K> keySet();
造成Hashtable和HashMap有关遍历方法的共性与差异的原因在于:共性是由于它们都实现了Map接口;差异在于Hashtable继承自Dictionary类而HashMap没有。
对于这些方法的记忆也很简单:
首先记Map接口的方法,因为Map的特性决定了key值和Entry值不能重复,而value值可以重复。所以Map接口对于key和Entry都返回set型,即keySet()和entrySet(),而value则为Collection型,即values();
Dictionary没有考虑这么多,key值直接通过Enumeration形式的keys()获得,value值通过Enumeration形式的elements()获得。
OK~
简介
packagejava.util;
importjava.io.*;
HashMap属于包java.util,实现中使用到了包java.io中的某些类。
它继承自AbstractMap,实现了Map接口,Cloneable接口,Serializable接口
默认值
staticfinal int DEFAULT_INITIAL_CAPACITY = 16;
默认容量大小为16
staticfinal int MAXIMUM_CAPACITY = 1 << 30;
最大值为2的30次方
staticfinal float DEFAULT_LOAD_FACTOR = 0.75f;
默认装载因子为0.75
transientEntry[] table;
Entry的数组,即实际存储key和value对的容器。
staticclass Entry<K,V> implements Map.Entry<K,V>{}
Entry实现了Map里的Entry接口。
四种构建方法
publicHashMap(int initialCapacity, float loadFactor)
publicHashMap(int initialCapacity)
publicHashMap()
publicHashMap(Map<? extends K, ? extends V> m)
hash的实现:
static int hash(int h) {
// This function ensures that hashCodesthat differ only by
// constant multiples at each bitposition have a bounded
// number of collisions (approximately8 at default load factor).
h ^= (h >>> 20) ^ (h>>> 12);
return h ^ (h >>> 7) ^ (h>>> 4);
}
只是二进制数本身的移位与异或运算。
索引的实现:
static int indexFor(int h, int length) {
return h & (length-1);
}
注意这里,就决定了length最好是2的n次方。
获取key对应的value
publicV get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> 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;
}
-----------end---------------
if(key == null) return getForNullKey();
这一句单独用于处理key为null的情况.这也是HashMap允许任何形式null值的原因所在.
如果key不为null,则获取key的hashCode,根据静态方法hash(int h)
h^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^(h >>> 4);
求得对应的hash值,然后用求得的hash值去table里找到该hash值对应的索引.
table是一个Entry数组,对应的table[i]即是一个Entry对象(实现了Map.Entry),里边有K,V,hash值,和next.
如果没有预先put进去,会是一个null值,即返回null; 如果有,则判断该处的hash值与key值是否与get(key)里的key一致,如果不一致则e= e.next();直到找到一致的或者e == null;
null值的处理
get:
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e!= null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}
get时,当key为null执行此方法.即是索引的0位置.
put:
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e!= null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}
由for循环中的循环体可知,如果table[0]位置不为null则直接将V替换掉原来Entry中的V;
否则,则在table[0]直接增加一个Entry;
put的实现 & hash值冲突的处理
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<K,V> 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;
}
如果key为null,则直接调用null值的put方法;
否则,计算hash值 => 找hash值在table中对应的索引=> 判断该索引是否为null =>
为null,直接添加;
不为null,判断hash值和key值是否相同(即使不同的hash值得到的索引i也可能相同),相同直接更新V,不相同则继续e = e.next();直到找到具有完全相同hash值和key值的e(Entry), 或e == null;
如果最终e == null; 而且未找到完全相同的Entry,则运行addEntry(hash, key,value, i);
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V>e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length); //增长方式: *2;
}
利用一个单链的方式处理hash冲突.
假如本来table[i]为A,且A.next() == null;
这时候put方法获取了一个hash值与A的相同,但key值与A的不同的参数,则利用获得的两个参数key和value以及hash值与索引i值组合生成一个Entry B, 则将B放入table[i],将B.next 指向A;
同理,put了一个C,则将C放入table[i],C.next指向B;
containsKey
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
很猥琐吧,还是少用点;
containsValue
public boolean containsValue(Object value) {
if(value == null)
return containsNullValue();
Entry[]tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
returnfalse;
}
private boolean containsNullValue() {
Entry[] tab = table;
for (int i = 0; i < tab.length ;i++)
for (Entry e = tab[i] ; e != null ;e = e.next)
if (e.value == null)
return true;
return false;
}
更猥琐.
默认值与增长方式:
默认值为16,且即使指定初值,大小也只能是第一个比指定值大的2^n!
增长方式, 只有addEntry和putAll中直接会调用
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}
/**
* Transfers all entries from current table to newTable.
*/
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
do {
Entry<K,V> next =e.next;
int i = indexFor(e.hash,newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
当扩容的时候要去遍历所有的Entry,所以事先指定容量是能节约很多时间的。
遍历方式:
遍历Entry,Set<Map.Entry<K,V>>;
publicSet<Map.Entry<K,V>> entrySet();
遍历value,Collection<V>;
publicCollection<V> values();
遍历key, Set<K>; //与Hashtable不同
publicSet<K> keySet();
其他的方法基本上都是基于这些方法的.
OK~
适用情况: 喜欢把Chromium放在非系统盘.
我的Chromium的启动参数为: "D:Program FilesChromiumchrome.exe" --user-data-dir=DATA --enable-apps
但是有时候点开本地的htm文件或者点开别人发送的网址时会以不加参数的情况启动.
这时候只需要改三处注册表项即可:
[HKEY_CLASSES_ROOTChromiumHTMLshellopencommand]
[HKEY_CLASSES_ROOThttpshellopencommand]
[HKEY_CLASSES_ROOThttpsshellopencommand]
将这三项中的默认值都改为:
"D:Program FilesChromiumchrome.exe" --user-data-dir=DATA --enable-apps -- "%1"
即可, -- "%1" 必选, 因为它表示输入的网址; 其他的参数自己看着弄吧.
强烈推荐!
相信不少人都喜欢用 “开始 - 运行”吧, 今天要介绍的工具可以看做是对 “运行” 的增强版。
详细的介绍已经在善用佳软发布: 神逸之作:国产快速启动软件神品ALTRun
最新的下载:http://code.google.com/p/altrun/
今天我使用之后发现只能用一个字来形容这款软件: 赞
一颗二叉查找树如果满足下面的红黑性质,则为一颗红黑树:
1) 每个结点或是红的,或是黑的.
2) 根结点是黑的.
3) 每个叶结点(NIL)是黑的. (孩子结点指向null的结点重定向到NIL)
4) 如果一个结点是红的,则它的两个儿子都是黑的.
5) 对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点.
由这个定义可知, 红黑树并不是平衡二叉树, 它只是一种近似平衡的二叉搜索树.
我们知道, 在动态构建二叉树的过程中, 一定要避免出现单支树的情况; 因为在那种急坏的情况下, 查找结点的时间复杂度会大大升高. 所以才引出了平衡二叉树, 但是广泛采用的构建平衡二叉树的两种方法各有各的缺点.
一种是利用一个平衡因子的方法: 平衡因子(Balance Factor,BF)定义为该节点的左子树的深度减去其右子树的深度, 则平衡二叉树上所有节点的平衡因子只可能是-1、0和1. 只要树上有一个节点的平衡因子的绝对值大于1, 则该二叉树就是不平衡的了. 利用平衡因子可以快速准确地判断二叉树的平衡状况, 但缺点在于每次插入新结点都要迭代地去更新父结点的平衡因子, 知道根节点.
另一种是利用一个标志位去存储当前的层数: 虽然这种方法操作起来方便, 但是在旋转时需要更新旋转结点的所有子树的标志位, 而且会占用大量的空间去存储这些信息.
所以, 第一种方法需要向上迭代到根, 第二种方法需要向下迭代到叶子. 在较坏情况下的效果都不会好.
而红黑树则没有这些缺点, 它在最坏的情况下也是只需要改变有限的几个结点的颜色.
下面看看红黑树的具体实现:
插入结点:
针对插入新结点, 如果它的父节点是黑色, 这时候不会违背五条性质中的任何一条.
当然, 我们关心的是那种它的父节点是红色的情况, 因为这时候它违背了性质4. 所以必须对树的结构进行一定的调整使其继续满足红黑树的五个性质.
因为要考虑新插入结点的父亲是左孩子还是右孩子, 这些结构在拓扑上是完全对称的, 所以我们只考虑它父亲是左孩子的情况.
需要改变树的结果, 有3×2 = 6种情况:
Case1:
B
/
R R
/ /
new
新插入的结点是红的, 它父亲是红的, 它父亲的兄弟也是红的.
违背了性质4, 这时候需要进行一个调整, 使其在满足性质4的情况下而不违背性质5.
染色: 父亲与叔父染成黑的, 爷爷染成红色;
这样从它爷爷的角度去看满足了性质4, 但不能保证整个树满足性质4 (因为爷爷的父亲可能是红色); 但从整棵树的角度去看肯定是满足性质5的, 因为没有在一个分支上加入多余的黑结点.
迭代: 为了保障整棵树的角度满足性质4, 需要将新插入结点的爷爷当作新插入的结点去考虑.
Case2 & Case3:
共同的前提: 新插入的结点是红的, 它父亲是红的, 但它父亲的兄弟是黑的.
我的推论: 这时候它父亲的兄弟是NIL, 即它的爷爷只有它父亲一个孩子. 因为它爷爷有两个孩子, 但左孩子是红色, 而右孩子不是红色, 这必然会违背性质5. 即在插入前, 从它的爷爷出发到它的父亲的叶子节点之间的路径上是没有黑色结点的, 而从它爷爷向着爷爷的右孩子出发到叶子节点之间至少有一个黑色结点. 所以违背了性质5.
Case2:新插入的结点是左孩子.
B
/
R B
/
new
Case3:新插入的结点是右孩子.
B
/
R B
/
new
对于Case3:对新插入结点的父亲进行左旋,进而转换为Case2.
Case2:违背了性质4,但不违背性质5.
这就要求进行一种旋转保障不违背性质5的前提下修正违背的性质4.
染色: 父亲染为黑的,爷爷染为红的.
旋转: 右旋爷爷.
B R B
/ / /
R NIL > B NIL > new R
/ / /
new new NIL
之后即满足了所有的5个性质.
删除结点:
如果被删除的结点是红色的,则当结点被删除后,红黑性质仍然得以保持,理由如下:
a)树中各结点的黑高度都没有变化.
b)不存在两个相邻的红色结点.
c)因为如果该节点是红的,就不可能是根,所以跟仍然是黑色的.
如果被删除的结点是黑色的, 则会产生三个问题
要删除的结点y,如果y有个不是NIL的孩子,则x为y的唯一孩子;如果y没有孩子,则x为NIL,把x的父节点(原来是y)赋值为y的父节点
①如果y原来是根结点,而y的一个红色的孩子成为了新的根,这就违反了性质2)。
②如果x和p[y](现在也是p[x])都是红的,就违反了性质4)。
③删除y将导致先前包含y的任何路径上黑结点个数少1。因此,性质5)被y的一个祖先破坏了。补救这个问题的一个办法就是把结点x视为还有额外的一重黑色。也就是说,如果将任意包含结点x的路径上黑结点的个数加1,则这种假设下,性质5)成立。当将黑节点y删除时,将其黑色“下推”至其子节点。现在问题就变为结点x可能既不是红,也不是黑,从而违反了性质1)。结点x是双重黑色或红黑,这就分别给包含x的路径上黑结点的贡献2个或1个。x的color属性仍然是red(如果x是红黑的)或者black(如果x是双重黑色)。换言之,一个结点额外的黑色反映在x指向它,而不是他的color属性。
四种情况
Case1:x的兄弟w是红的.
Case2:x的兄弟w是黑的,而且w的两个孩子都是黑的.
Case3:x的兄弟w是黑的,w的左孩子是红色的,右孩子是黑色的.
Case4:x的兄弟w是黑的,而且w的右孩子是红色的.
情况1:x的兄弟w是红色的
因为w必须有黑色的孩子,我们可以改变w和p[x]颜色,再对p[x]做一次左旋,从而红黑性质得以继续保持。现在,x的新兄弟是旋转之前w的某个孩子,其颜色为黑色。这样,我们已经将情况1)转化为情况2), 3)或4)了.
情况2:x的兄弟w是黑色的,而且w的两个孩子都是黑色的
因为w也是黑色的,故从x和w上去掉一重黑色,从而x只有一重黑色而w为红色。为了补偿从x和w中去掉一重黑色,我们想在原来是红色或者黑色的p[x]内新增一重额外的黑色。通过以p[x]为新结点x来恢复while循环。注意如果从情况1进入情况2,则新结点x是红黑色的,因为原来的p[x]是红色的。因此,新结点x的color属性的值c为red,并且在测试循环条件后循环结束。然后新结点x在第23行中被(单独)着为黑色。
情况3:x的兄弟w是黑色的,w的左孩子是红色的,右孩子是黑色的
可以交换w和其左孩子left[w]的颜色,并对w进行右旋,而红黑性质仍然保持。现在x的新兄弟w是一个有红色右孩子的黑节点,这样我们从情况3转换成情况4
情况4:x的兄弟w是黑色的,而且w的右孩子是红色的
通过做某些颜色修改并对p[x]做一次左旋,可以去掉x的额外黑色来把它变成单独黑色,而不破坏红黑性质。将x置为根后,当while循环测试其循环条件时循环结束。
本文参考了:
《算法导论》
《算法导论》笔记--红黑树(一) http://liyiwen.javaeye.com/blog/345800
《算法导论》笔记--红黑树(二) http://liyiwen.javaeye.com/blog/345799
抄袭了:
算法导论 红黑树 http://www.886s.com/blog/?p=30
如果你在找类似safari 5的阅读器,不要犹豫,果断的去下载吧!
地址: https://chrome.google.com/extensions/detail/ppelffpjgkifjfgnbaaldcehkpajlmbc
可能有人并不知道safari 5的阅读器功能。
但这些情况你肯定遇到过:看新闻或者博客时字体太小! 广告太多! 阅读效率低下!
有了这个插件就不用愁了,它可以把你想看的内容提取出来显示。
相当大的字体,没有广告,自动翻页,阅读新闻/博客必备。
以前没有这个插件的时候我都是专门装个safari 5来看新闻。
附:改变阅读习惯的Safari 5阅读器!http://imtx.cn/archives/1502.html
在一天的24小时之中,时钟的时针、分针和秒针完全重合在一起的时候有几次?
我们做几个假设:
1.一个时钟有60个刻度;
2.秒针从0移动到60,分针移动一个刻度;
3.分针从0开始每移动12个刻度,时针则移动1个刻度.
结果是24个重合的时刻.
以下是java代码:
public static void main(String args[]) {
final int n = 3600 * 24;int i1 = 0, i2 = 0, i3 = 0;// 分别代表时针,分针,和秒针指向的刻度.int x = 0;int sum = 0;for (int i = 0; i < n; i++) {i3 = i % 60;// 秒针指向的刻度i2 = (i / 60) % 60;// 走过的分钟数除60的余数.i1 = (i / 3600) * 5 + i2 / 12;// 小时数*5+分钟数/12if (((i1 < 60 ? i1 : i1 % 60) == i2) && (i2 == i3)) {System.out.println(String.format("%02d", (i1 / 5)) + ":"+ String.format("%02d", i2) + ":"+ String.format("%02d", i3) + ", "+ String.format("%05d", i) + ", "+ String.format("%04d", (i - x)));x = i;sum++;}}System.out.println(sum);}
结果:
00:00:00, 00000, 0000
01:05:05, 03905, 3905
02:10:10, 07810, 3905
03:16:16, 11776, 3966
04:21:21, 15681, 3905
05:27:27, 19647, 3966
06:32:32, 23552, 3905
07:38:38, 27518, 3966
08:43:43, 31423, 3905
09:49:49, 35389, 3966
10:54:54, 39294, 3905
11:59:59, 43199, 3905
12:00:00, 43200, 0001
13:05:05, 47105, 3905
14:10:10, 51010, 3905
15:16:16, 54976, 3966
16:21:21, 58881, 3905
17:27:27, 62847, 3966
18:32:32, 66752, 3905
19:38:38, 70718, 3966
20:43:43, 74623, 3905
21:49:49, 78589, 3966
22:54:54, 82494, 3905
23:59:59, 86399, 3905
24