一辈子的程序员?

爱你一生不变-芳芳!
posts - 27, comments - 15, trackbacks - 0, articles - 0
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

我们已经知道,在 document 对象中有一个 cookie 属性。但是 Cookie 又是什么?“某些 Web 站点在您的硬盘上用很小的文本文件存储了一些信息,这些文件就称为 Cookie。”—— MSIE 帮助。一般来说,Cookies 是 CGI 或类似,比 HTML 高级的文件、程序等创建的,但是 javascript 也提供了对 Cookies 的很全面的访问权利。

  我们先要学一学 Cookie 的基本知识。

  每个 Cookie 都是这样的:<cookie名>=<值>

  <cookie名>的限制与 javascript 的命名限制大同小异,少了“不能用 javascript 关键字”,多了“只能用可以用在 URL 编码中的字符”。后者比较难懂,但是只要你只用字母和数字命名,就完全没有问题了。<值>的要求也是“只能用可以用在 URL 编码中的字符”。

  每个 Cookie 都有失效日期,一旦电脑的时钟过了失效日期,这个 Cookie 就会被删掉。我们不能直接删掉一个 Cookie,但是可以用设定失效日期早于现在时刻的方法来间接删掉它。

  每个网页,或者说每个站点,都有它自己的 Cookies,这些 Cookies 只能由这个站点下的网页来访问,来自其他站点或同一站点下未经授权的区域的网页,是不能访问的。每一“组”Cookies 有规定的总大小(大约 2KB 每“组”),一超过最大总大小,则最早失效的 Cookie 先被删除,来让新的 Cookie“安家”。

  现在我们来学习使用 documents.cookie 属性。

  如果直接使用 document.cookie 属性,或者说,用某种方法,例如给变量赋值,来获得 document.cookie 的值,我们就可以知道在现在的文档中有多少个 Cookies,每个 Cookies 的名字,和它的值。例如,在某文档中添加“document.write(document.cookie)”,结果显示:

name=kevin; email=kevin@kevin.com; lastvisited=index.html

这意味着,文档包含 3 个 Cookies:name, email 和 lastvisited,它们的值分别是 kevin, kevin@kevin.com 和 index.html。可以看到,两个 Cookies 之间是用分号和空格隔开的,于是我们可以用 cookieString.split('; ') 方法得到每个 Cookie 分开的一个数组(先用 var cookieString = document.cookie)。

  设定一个 Cookie 的方法是对 documents.cookie 赋值。与其它情况下的赋值不同,向 documents.cookie 赋值不会删除掉原有的 Cookies,而只会增添 Cookies 或更改原有 Cookie。赋值的格式:
documents.cookie = 'cookieName=' + escape('cookievalue')
+ ';expires=' + expirationDateObj.toGMTString();
是不是看到头晕了呢?cookieName 表示 Cookie 的名称,cookievalue 表示 Cookie 的值,expirationDateObj 表示储存着失效日期的日期对象名,如果不需要指定失效日期,则不需要第二行。不指定失效日期,则浏览器默认是在关闭浏览器(也就是关闭所有窗口)之后过期。

  首先 escape() 方法:为什么一定要用?因为 Cookie 的值的要求是“只能用可以用在 URL 编码中的字符”。我们知道“escape()”方法是把字符串按 URL 编码方法来编码的,那我们只需要用一个“escape()”方法来处理输出到 Cookie 的值,用“unescape()”来处理从 Cookie 接收过来的值就万无一失了。而且这两个方法的最常用途就是处理 Cookies。其实设定一个 Cookie 只是“documents.cookie = 'cookieName=cookievalue'”这么简单,但是为了避免在 cookievalue 中出现 URL 里不准出现的字符,还是用一个 escape() 好。
  然后“expires”前面的分号:注意到就行了。是分号而不是其他。
  最后 toGMTString() 方法:设定 Cookie 的时效日期都是用 GMT 格式的时间的,其它格式的时间是没有作用的。

  现在我们来实战一下。设定一个“name=rose”的 Cookie,在 3 个月后过期。
var expires = new Date();
expires.setTime(expires.getTime() + 3 * 30 * 24 * 60 * 60 * 1000);
/* 三个月 x 一个月当作 30 天 x 一天 24 小时
x 一小时 60 分 x 一分 60 秒 x 一秒 1000 毫秒 */
documents.cookie = 'name=rose;expires=' + expires.toGMTString();

为什么没有用 escape() 方法?这是因为我们知道 rose 是一个合法的 URL 编码字符串,也就是说,'rose' == escape('rose')。一般来说,如果设定 Cookie 时不用 escape(),那获取 Cookie 时也不用 unescape()。

  再来一次:编写一个函数,作用是查找指定 Cookie 的值。
function getCookie(cookieName) {
var cookieString = documents.cookie;
var start = cookieString.indexOf(cookieName + '=');
// 加上等号的原因是避免在某些 Cookie 的值里有
// 与 cookieName 一样的字符串。
if (start == -1) // 找不到
return null;
start += cookieName.length + 1;
var end = cookieString.indexOf(';', start);
if (end == -1) return unescape(cookieString.substring(start));
return unescape(cookieString.substring(start, end));
}

这个函数用到了字符串对象的一些方法,如果你不记得了(你是不是这般没记性啊),请快去查查。这个函数所有的 if 语句都没有带上 else,这是因为如果条件成立,程序运行的都是 return 语句,在函数里碰上 return,就会终止运行,所以不加 else 也没问题。该函数在找到 Cookie 时,就会返回 Cookie 的值,否则返回“null”。

  现在我们要删除刚才设定的 name=rose Cookie。
var expires = new Date();
expires.setTime(expires.getTime() - 1);
documents.cookie = 'name=rose;expires=' + expires.toGMTString();

posted @ 2006-08-18 15:11 boddi 阅读(136) | 评论 (0)编辑 收藏

Java 多线程程序设计要点(synchronized)
Tag:JAVA基础

多线程程序设计要点:

  1.多线程中有主内存和工作内存之分, 在JVM中,有一个主内存,专门负责所有线程共享数据;而每个线程都有他自己私有的工作内存, 主内存和工作内存分贝在JVM的stack区和heap区。

  2.线程的状态有'Ready', 'Running', 'Sleeping', 'Blocked', 和 'Waiting'几个状态,
'Ready' 表示线程正在等待CPU分配允许运行的时间。

  3.线程运行次序并不是按照我们创建他们时的顺序来运行的,CPU处理线程的顺序是不确定的,如果需要确定,那么必须手工介入,使用setPriority()方法设置优先级。

  4.我们无从知道一个线程什么时候运行,两个或多个线程在访问同一个资源时,需要synchronized

  5. 每个线程会注册自己,实际某处存在着对它的引用,因此,垃圾回收机制对它就“束手无策”了。

  6. Daemon线程区别一般线程之处是:主程序一旦结束,Daemon线程就会结束。

  7. 一个对象中的所有synchronized方法都共享一把锁,这把锁能够防止多个方法对通用内存同时进行的写操作。synchronized static方法可在一个类范围内被相互间锁定起来。

  8. 对于访问某个关键共享资源的所有方法,都必须把它们设为synchronized,否则就不能正常工作。

  9. 假设已知一个方法不会造成冲突,最明智的方法是不要使用synchronized,能提高些性能。

  10. 如果一个\"同步"方法修改了一个变量,而我们的方法要用到这个变量(可能是只读),最好将自己的这个方法也设为 synchronized。

  11. synchronized不能继承,  父类的方法是synchronized,那么其子类重载方法中就不会继承“同步”。

  12. 线程堵塞Blocked有几个原因造成:

  (1)线程在等候一些IO操作
  (2)线程试图调用另外一个对象的“同步”方法,但那个对象处于锁定状态,暂时无法使用。

  13.原子型操作(atomic), 对原始型变量(primitive)的操作是原子型的atomic. 意味着这些操作是线程安全的, 但是大部分情况下,我们并不能正确使用,来看看 i = i + 1 , i是int型,属于原始型变量:

  (1)从主内存中读取i值到本地内存.
  (2)将值从本地内存装载到线程工作拷贝中.
  (3)装载变量1.
  (4)将i 加 1.
  (5)将结果给变量i.
  (6)将i保存到线程本地工作拷贝中.
  (7)写回主内存.

  注意原子型操作只限于第1步到第2步的读取以及第6到第7步的写, i的值还是可能被同时执行i=i+1的多线程中断打扰(在第4步)。

  double 和long 变量是非原子型的(non-atomic)。数组是object 非原子型。

  14. 由于13条的原因,我们解决办法是:

  class xxx extends Thread{
   //i会被经常修改
  private int i;

  public synchronized int read(){ return i;}

  public synchronized void update(){ i = i + 1;}

  ..........

  }
 

  15. Volatile变量, volatile变量表示保证它必须是与主内存保持一致,它实际是"变量的同步", 也就是说对于volatile变量的操作是原子型的,如用在long 或 double变量前。

  16. 使用yield()会自动放弃CPU,有时比sleep更能提升性能。

  17. sleep()和wait()的区别是:wait()方法被调用时会解除锁定,但是我们能使用它的地方只是在一个同步的方法或代码块内。

  18. 通过制造缩小同步范围,尽可能的实现代码块同步,wait(毫秒数)可在指定的毫秒数可退出wait;对于wait()需要被notisfy()或notifyAll()踢醒。

  19. 构造两个线程之间实时通信的方法分几步:
  (1). 创建一个PipedWriter和一个PipedReader和它们之间的管道;
  PipedReader in = new PipedReader(new PipedWriter())
  (2). 在需要发送信息的线程开始之前,将外部的PipedWriter导向给其内部的Writer实例out
  (3). 在需要接受信息的线程开始之前,将外部的PipedReader导向给其内部的Reader实例in
  (4). 这样放入out的所有东西度可从in中提取出来。

  20. synchronized带来的问题除性能有所下降外,最大的缺点是会带来死锁DeadLock,只有通过谨慎设计来防止死锁,其他毫无办法,这也是线程难以驯服的一个原因。不要再使用stop() suspend() resume()和destory()方法

  21. 在大量线程被堵塞时,最高优先级的线程先运行。但是不表示低级别线程不会运行,运行概率小而已。

  22. 线程组的主要优点是:使用单个命令可完成对整个线程组的操作。很少需要用到线程组。

  23. 从以下几个方面提升多线程的性能:

  检查所有可能Block的地方,尽可能的多的使用sleep或yield()以及wait();

  尽可能延长sleep(毫秒数)的时间;

  运行的线程不用超过100个,不能太多;

  不同平台linux或windows以及不同JVM运行性能差别很大。


shiweifu 发表于 22:24:04  

posted @ 2006-08-15 14:46 boddi 阅读(178) | 评论 (0)编辑 收藏

attachEvent() / addEventListener() 对象添加触发事件(转)

有时候当某一对象的某一事件被触发时,它所要执行的程序可能是一大串,有可能是要呼叫某一函数,也有可能同时又要呼叫另一函数。

document.getElementById("btn").onclick = method1;
document.getElementById("btn").onclick = method2;
document.getElementById("btn").onclick = method3;
如果这样写,那么将会只有medhot3被执行

在IE中使用addachEvent ,

var btn1Obj = document.getElementById("btn1");
//object.attachEvent(event,function);
btn1Obj.attachEvent("onclick",method1);
btn1Obj.attachEvent("onclick",method2);
btn1Obj.attachEvent("onclick",method3);
执行顺序为method3->method2->method1

Mozilla系列中需要使用 addEventListener

var btn1Obj = document.getElementById("btn1");
//element.addEventListener(type,listener,useCapture);
btn1Obj.addEventListener("click",method1,false);
btn1Obj.addEventListener("click",method2,false);
btn1Obj.addEventListener("click",method3,false);
执行顺序为method1->method2->method3

看看gmail的代码

var Ka=navigator.userAgent.toLowerCase();
var rt=Ka.indexOf("opera")!=-1;
var r=Ka.indexOf("msie")!=-1&&(document.all&&!rt);

function Zl(a,b,c){if(r){a.attachEvent("on"+b,c)}else{a.addEventListener(b,c,false)}}

posted @ 2006-08-15 09:47 boddi 阅读(5548) | 评论 (1)编辑 收藏

javascript事件列表解说

 
javascript事件列表解说
事件浏览器支持解说
一般事件onclickIE3、N2 鼠标点击时触发此事件
ondblclickIE4、N4 鼠标双击时触发此事件
onmousedownIE4、N4 按下鼠标时触发此事件
onmouseupIE4、N4 鼠标按下后松开鼠标时触发此事件
onmouseoverIE3、N2 当鼠标移动到某对象范围的上方时触发此事件
onmousemoveIE4、N4 鼠标移动时触发此事件
onmouseoutIE4、N3当鼠标离开某对象范围时触发此事件
onkeypressIE4、N4 当键盘上的某个键被按下并且释放时触发此事件.
onkeydownIE4、N4 当键盘上某个按键被按下时触发此事件
onkeyupIE4、N4 当键盘上某个按键被按放开时触发此事件
页面相关事件onabortIE4、N3 图片在下载时被用户中断
onbeforeunloadIE4、N 当前页面的内容将要被改变时触发此事件
onerrorIE4、N3 出现错误时触发此事件
onloadIE3、N2 页面内容完成时触发此事件
onmoveIE、N4 浏览器的窗口被移动时触发此事件
onresizeIE4、N4 当浏览器的窗口大小被改变时触发此事件
onscrollIE4、N 浏览器的滚动条位置发生变化时触发此事件
onstopIE5、N 浏览器的停止按钮被按下时触发此事件或者正在下载的文件被中断
onunloadIE3、N2 当前页面将被改变时触发此事件
表单相关事件onblurIE3、N2 当前元素失去焦点时触发此事件
onchangeIE3、N2 当前元素失去焦点并且元素的内容发生改变而触发此事件
onfocusIE3 、N2当某个元素获得焦点时触发此事件
onresetIE4 、N3 当表单中RESET的属性被激发时触发此事件
onsubmitIE3 、N2 一个表单被递交时触发此事件
滚动字幕事件onbounceIE4、N在Marquee内的内容移动至Marquee显示范围之外时触发此事件
onfinishIE4、N当Marquee元素完成需要显示的内容后触发此事件
onstartIE4、 N当Marquee元素开始显示内容时触发此事件
编辑事件onbeforecopyIE5、N当页面当前的被选择内容将要复制到浏览者系统的剪贴板前触发此事件
onbeforecutIE5、 N当页面中的一部分或者全部的内容将被移离当前页面[剪贴]并移动到浏览者的系统剪贴板时触发此事件
onbeforeeditfocusIE5、N当前元素将要进入编辑状态
onbeforepasteIE5、 N内容将要从浏览者的系统剪贴板传送[粘贴]到页面中时触发此事件
onbeforeupdateIE5、 N当浏览者粘贴系统剪贴板中的内容时通知目标对象
oncontextmenuIE5、N当浏览者按下鼠标右键出现菜单时或者通过键盘的按键触发页面菜单时触发的事件
oncopyIE5、N当页面当前的被选择内容被复制后触发此事件
oncutIE5、N 当页面当前的被选择内容被剪切时触发此事件
ondragIE5、N 当某个对象被拖动时触发此事件 [活动事件]
ondragdropIE、N4一个外部对象被鼠标拖进当前窗口或者帧
ondragendIE5、N当鼠标拖动结束时触发此事件,即鼠标的按钮被释放了
ondragenterIE5、N当对象被鼠标拖动的对象进入其容器范围内时触发此事件
ondragleaveIE5、N 当对象被鼠标拖动的对象离开其容器范围内时触发此事件
ondragoverIE5、N当某被拖动的对象在另一对象容器范围内拖动时触发此事件
ondragstartIE4、N当某对象将被拖动时触发此事件
ondropIE5、N在一个拖动过程中,释放鼠标键时触发此事件
onlosecaptureIE5、N当元素失去鼠标移动所形成的选择焦点时触发此事件
onpasteIE5、N当内容被粘贴时触发此事件
onselect IE4、N当文本内容被选择时的事件
onselectstartIE4、N当文本内容选择将开始发生时触发的事件
数据绑定onafterupdateIE4、N当数据完成由数据源到对象的传送时触发此事件
oncellchangeIE5、N当数据来源发生变化时
ondataavailableIE4、N当数据接收完成时触发事件
ondatasetchangedIE4、N数据在数据源发生变化时触发的事件
ondatasetcompleteIE4、N当来子数据源的全部有效数据读取完毕时触发此事件
onerrorupdateIE4、N当使用onBeforeUpdate事件触发取消了数据传送时,代替onAfterUpdate事件
onrowenterIE5、N当前数据源的数据发生变化并且有新的有效数据时触发的事件
onrowexitIE5、N当前数据源的数据将要发生变化时触发的事件
onrowsdeleteIE5、N当前数据记录将被删除时触发此事件
onrowsinsertedIE5、N当前数据源将要插入新数据记录时触发此事件
外部事件onafterprintIE5、N当文档被打印后触发此事件
onbeforeprintIE5、N当文档即将打印时触发此事件
onfilterchangeIE4、N当某个对象的滤镜效果发生变化时触发的事件
onhelpIE4、N当浏览者按下F1或者浏览器的帮助选择时触发此事件
onpropertychangeIE5、N当对象的属性之一发生变化时触发此事件
onreadystatechangeIE4、N当对象的初始化属性值发生变化时触发此事件
onactivate 当对象设置为活动元素时触发。
onafterupdate 当成功更新数据源对象中的关联对象后在数据绑定对象上触发。
onbeforedeactivate 在 activeElement 从当前对象变为父文档其它对象之前立即触发。
onbeforeupdate 当成功更新数据源对象中的关联对象前在数据绑定对象上触发。
onblur 在对象失去输入焦点时触发。
oncontrolselect 当用户将要对该对象制作一个控件选中区时触发。
ondeactivate 当 activeElement 从当前对象变为父文档其它对象时触发。
onerrorupdate 更新数据源对象中的关联数据出错时在数据绑定对象上触发。
onfocus 当对象获得焦点时触发。
onload 在浏览器完成对象的装载后立即触发。
onmove 当对象移动时触发。
onmoveend 当对象停止移动时触发。
onmovestart 当对象开始移动时触发。
onreadystatechange 当对象状态变更时触发。
onresizeend 当用户更改完控件选中区中对象的尺寸时触发。
onresizestart 当用户开始更改控件选中区中对象的尺寸时触发。
ontimeerror 当特定时间错误发生时无条件触发,通常由将属性设置为无效值导致。:

JavaScript 事件串联执行多个处理过程的方法

JavaScript 事件串联执行多个处理过程的方法

2006-01-04 @ 15:46:42 · 作者 andot · 归类于 JavaScript

以前写 JavaScript 程序时,事件都是采用

object . event = handler ;

的方式初始化。这种方式对于 Internet Explorer、Mozilla/Firefox 和 Opera 来说很通用。但是有一个问题就是,这种方式只能一个事件对应一个事件处理过程。如果希望一个事件可以依次执行多个处理过程就不好用了。

但是 Internet Explorer 从 5.0 开始提供了一个 attachEvent 方法,使用这个方法,就可以给一个事件指派多个处理过程了。attachEvent 对于目前的 Opera 也适用。但是问题是 Mozilla/Firefox 并不支持这个方法。但是它支持另一个 addEventListener 方法,这个方法跟 attachEvent 差不多,也是用来给一个事件指派多个处理过程的。但是它们指派的事件有些区别,在 attachEvent 方法中,事件是以 “on” 开头的,而在 addEventListener 中,事件没有开头的 “on”,另外 addEventListener 还有第三个参数,一般这个参数指定为 false 就可以了。

因此要想在你的程序中给一个事件指派多个处理过程的话,只要首先判断一下浏览器,然后根据不同的浏览器,选择使用 attachEvent 还是 addEventListener 就可以了。实例如下:

if ( document . all ) {
    
window . attachEvent ( ' onload ' , handler1 ) ;
    
window . attachEvent ( ' onload ' , handler2 ) ;
}
else {
    
window . addEventListener ( ' load ' , handler1 , false ) ;
    
window . addEventListener ( ' load ' , handler2 , false ) ;
}

注意:attachEvent 所指派的多个过程的执行顺序是随机的,所以这几个过程之间不要有顺序依赖。另外 attachEvent 和 addEventListener 不仅仅适用于 window 对象,其他的一些对象也支持该方法。

posted @ 2006-08-10 20:43 boddi 阅读(261) | 评论 (0)编辑 收藏

在Java 2的Collections框架中,主要包括两个接口及其扩展和实现类:Collection接口和Map接口。两者的区别在于前者存储一组对象,后者则存储一些关键字/值对。

public interface java.util.Map {

    //Altering Methods
      public Object put(Object key, Object value);    
      public Object remove(Object key);              
      public void putAll(java.util.Map);             
      public void clear();   

    //Querying Methods
      public Object get(Object key);           
      public int size();                     
      public boolean isEmpty();                  
      public boolean containsKey(Object);          
      public boolean containsValue(Object);          
      public boolean equals(Object);                 

    //Viewing Methods
      public java.util.Set keySet();                  //Gets keys
      public java.util.Collection values();           //Gets values
      public java.util.Set entrySet();                //Gets mappings

      public static interface java.util.Map.Entry {   //a map-entry (single key/value pair)
         public Object getKey();                    //returns current entry key
         public Object getValue();                  //returns current entry value
        public Object setValue(Object value);      
  public boolean equals(Object);             
  public int hashCode();                         }
}
Map接口提供了方便易用的方法,通过这些方法可以查询、查看、修改当前Map的内容。注意对于Map接口的keySet()方法返回一个Set,Set是Collection接口的一个扩展,包含不重复的一组对象。因为Map中的key是不可重复的,所以得到所有key的keySet()方法返回一个Set对象。Map接口本身还包含了一个Map.Entry接口,一个Map.Entry就是Map中的一个关键字/值对。Map接口中的entrySet()方法就返回了一个集合对象,其中每一个元素都实现了Map.Entry接口。Map接口的get(Object key),put(Object key,Object value),和remove(Object key)方法都有同一个问题。他们的返回类型都是Object,当返回null时,可以猜测为调用那个方法前那个key不存在。但是只有在null不允许作为Map的值时可以这样猜测。所有Map接口的通用实现都允许null作为key或者value,这就说当返回一个null值,就可以意味着很多事情。只是因为通用实现允许null值,你不能下那个映射有null值的结论。如果你确知没有null值,那返回null值就意味着调用那个方法前,映射里并没有那个键。否则,你必须调用containsKey(Object key)来看看那个Key是否存在。

Hashtable


java.util.Hashtable实现了Map接口,在Hashtable中使用key对象的hashCode()作为对应的对象的相对存储地址,以便实现根据关键字快速查找对象的功能。所以只有一个实现了hashCode()和equals()方法的对象才可作为Hashtable的key。null值不能作为关键字或值。
public class java.util.Hashtable extends Dictionary implements Cloneable, Map, Serializable {

     //Hashtable constructors
     //construct a default Hashtable with default capacity and load of 0.75
     public Hashtable();                     
     //construct a Hashtable with passed capacity and default load of 0.75 
     public Hashtable (int initialCapacity); 
     //construct Hashtable with passed capacity and load 
     public Hashtable(int initialCapacity, float load); 
     //construct Hashtable with passed mapping 
     public Hashtable(Map);                  
     
     //Hashtable specific methods
     //checks if Object is in Hashtable 
     public boolean contains(Object);        
     //returns Enumeration of elements in Hashtable 
     public Enumeration elements();          
     //returns Enumeration of keys in hashtable
     public Enumeration keys();              
     //creates shallow copy of Hashtable(structure copied, but not key/values)
     public Object clone();                  
     //prints out key/value pairs of Hashtable elements
     public String toString();               
     //reorganizes all elements in Hashtable, and increases Hashtable capacity 
     protected void rehash();                
     
     //get Value from passed in key 
     public Object get(Object);              
     //insert key/value pair
     public Object put(Object key, Object value);      

}
Hashtable是Java 2集合框架推出之前的一个老的工具类,在新的Java 2集合框架下,已经被HashMap取代。Hashtable和HashMap的区别主要是前者是同步的,后者是快速失败机制保证不会出现多线程并发错误(Fast-Fail)。在初始化一个Hashtable时,可以指定两个参数:初始容量、负荷,这两个参数强烈的影响着Hashtable的性能。容量是指对象的个数,负荷是指散列表中的实际存储的对象个数和容量的比率。如果初始容量太小,那么Hashtable需要不断的扩容并rehash(),而这是很耗时的;如果初始容量太大,又会造成空间的浪费。负荷则相反,负荷太小会造成空间浪费,负荷太大又会耗时(因为这会造成较多的关键字的散列码重复,Hashtable使用一个链接表来存储这些重复散列码的对象)。容量的缺省值是11,负荷的缺省值是0.75,一般情况下你都可以使用缺省值来生成一个Hashtable。另外,在Hashtable中的大部分的方法都是同步的。

HashMap


HashMap基本实现了Map接口的全部方法。方法的签名大家看上面的Map接口。这儿主要说说几个Map接口中的方法。
按照集合框架的实现,哈希表是单链表作为元素的数组,有着同样索引值的两个或更多入口被一起链结到单链表中。哈希表声明如下:
    private Entry[] table;
组件类型Entry是Map.Entry接口的实现,Map.Entry声明于Map接口内。下边是Map.Entry接口的简化实现:
    private static class Entry implements Map.Entry{
        int hashCode;
        Object key;
        Object value;
        Entry next;

        Entry(int hashCode,Object key,Object value,Entry next){
            This.hashCode=hashCode;
            This.key=key;
            This.value=value;
            This.next=next;
}
public Object getKey(){
    return key;
}
public Object getValue(){
    return value;
}
public Object setValue(Object value){
    Object oldValue=this.value;
    This.value=value;
    Return oldValue;
}
}

SortedMap是Map接口的子接口,SortedMap的标准实现是TreeMap,实现了一个排序的Map。这儿不再做说明。本站翻译的《Java 规则》(Java Rules)中对Java2的集合框架有深入的研究。本文限于篇幅,只及皮毛。有兴趣的朋友,请参考Java2源码中的集合实现代码和API doc。

posted @ 2006-08-05 17:15 boddi 阅读(374) | 评论 (0)编辑 收藏

如何优化JavaScript脚本的性能

随着网络的发展,网速和机器速度的提高,越来越多的网站用到了丰富客户端技术。而现在Ajax则是最为流行的一种方式。JavaScript是一种解释型语言,所以能无法达到和C/Java之类的水平,限制了它能在客户端所做的事情,为了能改进他的性能,我想基于我以前给JavaScript做过的很多测试来谈谈自己的经验,希望能帮助大家改进自己的JavaScript脚本性能。

 

语言层次方面

循环

循环是很常用的一个控制结构,大部分东西要依靠它来完成,在JavaScript中,我们可以使用for(;;),while(),for(in)三种循环,事实上,这三种循环中for(in)的效率极差,因为他需要查询散列键,只要可以就应该尽量少用。for(;;)和while循环的性能应该说基本(平时使用时)等价。

而事实上,如何使用这两个循环,则有很大讲究。最后得出的结论是:

  • 如果是循环变量递增或递减,不要单独对循环变量赋值,应该在它最后一次读取的时候使用嵌套的++或—操作符。

  • 如果要与数组的长度作比较,应该事先把数组的length属性放入一个局部变量中,减少查询次数。

局部变量和全局变量

局部变量的速度要比全局变量的访问速度更快,因为全局变量其实是全局对象的成员,而局部变量是放在函数的栈当中的。

不使用Eval

使用eval相当于在运行时再次调用解释引擎对内容进行运行,需要消耗大量时间。这时候使用JavaScript所支持的闭包可以实现函数模版(关于闭包的内容请参考函数式编程的有关内容)

减少对象查找

因为JavaScript的解释性,所以a.b.c.d.e,需要进行至少4次查询操作,先检查a再检查a中的b,再检查b中的c,如此往下。所以如果这样的表达式重复出现,只要可能,应该尽量少出现这样的表达式,可以利用局部变量,把它放入一个临时的地方进行查询。

这一点可以和循环结合起来,因为我们常常要根据字符串、数组的长度进行循环,而通常这个长度是不变的,比如每次查询a.length,就要额外进行一个操作,而预先把var len=a.length,则就少了一次查询。

字符串连接

如果是追加字符串,最好使用s+=anotherStr操作,而不是要使用s=s+anotherStr。

如果要连接多个字符串,应该少使用+=,如

s+=a;
s+=b;
s+=c;

应该写成

s+=a + b + c;

而如果是收集字符串,比如多次对同一个字符串进行+=操作的话,最好使用一个缓存。怎么用呢?使用JavaScript数组来收集,最后使用join方法连接起来,如下

var buf = new Array();
for(var i = 0; i < 100; i++){
buf.push(i.toString());
}
var all = buf.join("");

类型转换

类型转换是大家常犯的错误,因为JavaScript是动态类型语言,你不能指定变量的类型。

1. 把数字转换成字符串,应用"" + 1,虽然看起来比较丑一点,但事实上这个效率是最高的,性能上来说:

("" +) > String() > .toString() > new String()

这条其实和下面的“直接量”有点类似,尽量使用编译时就能使用的内部操作要比运行时使用的用户操作要快。

String()属于内部函数,所以速度很快,而.toString()要查询原型中的函数,所以速度逊色一些,new String()用于返回一个精确的副本。

2. 浮点数转换成整型,这个更容易出错,很多人喜欢使用parseInt(),其实parseInt()是用于将字符串转换成数字,而不是浮点数和整型之间的转换,我们应该使用Math.floor()或者Math.round()。

另外,和第二节的对象查找中的问题不一样,Math是内部对象,所以Math.floor()其实并没有多少查询方法和调用的时间,速度是最快的。

3. 对于自定义的对象,如果定义了toString()方法来进行类型转换的话,推荐显式调用toString(),因为内部的操作在尝试所有可能性之后,会尝试对象的toString()方法尝试能否转化为String,所以直接调用这个方法效率会更高

使用直接量

其实这个影响倒比较小,可以忽略。什么叫使用直接量,比如,JavaScript支持使用[param,param,param,...]来直接表达一个数组,以往我们都使用new Array(param,param,...),使用前者是引擎直接解释的,后者要调用一个Array内部构造器,所以要略微快一点点。

同样,var foo = {}的方式也比var foo = new Object();快,var reg = /../;要比var reg=new RegExp()快。

字符串遍历操作

对字符串进行循环操作,譬如替换、查找,应使用正则表达式,因为本身JavaScript的循环速度就比较慢,而正则表达式的操作是用C写成的语言的API,性能很好。

高级对象

自定义高级对象和Date、RegExp对象在构造时都会消耗大量时间。如果可以复用,应采用缓存的方式。

DOM相关

插入HTML

很多人喜欢在JavaScript中使用document.write来给页面生成内容。事实上这样的效率较低,如果需要直接插入HTML,可以找一个容器元素,比如指定一个div或者span,并设置他们的innerHTML来将自己的HTML代码插入到页面中。

对象查询

使用[“”]查询要比.items()更快,这和前面的减少对象查找的思路是一样的,调用.items()增加了一次查询和函数的调用。

创建DOM节点

通常我们可能会使用字符串直接写HTML来创建节点,其实这样做

  1. 无法保证代码的有效性

  2. 字符串操作效率低

所以应该是用document.createElement()方法,而如果文档中存在现成的样板节点,应该是用cloneNode()方法,因为使用createElement()方法之后,你需要设置多次元素的属性,使用cloneNode()则可以减少属性的设置次数——同样如果需要创建很多元素,应该先准备一个样板节点。

定时器

如果针对的是不断运行的代码,不应该使用setTimeout,而应该是用setInterval。setTimeout每次要重新设置一个定时器。

其他

脚本引擎

据我测试Microsoft的JScript的效率较Mozilla的Spidermonkey要差很多,无论是执行速度还是内存管理上,因为JScript现在基本也不更新了。但SpiderMonkey不能使用ActiveXObject

文件优化

文件优化也是一个很有效的手段,删除所有的空格和注释,把代码放入一行内,可以加快下载的速度,注意,是下载的速度而不是解析的速度,如果是本地,注释和空格并不会影响解释和执行速度。

总结

本文总结了我在JavaScript编程中所找到的提高JavaScript运行性能的一些方法,其实这些经验都基于几条原则:

  1. 直接拿手头现成的东西比较快,如局部变量比全局变量快,直接量比运行时构造对象快等等。

  2. 尽可能少地减少执行次数,比如先缓存需要多次查询的。

  3. 尽可能使用语言内置的功能,比如串链接。

  4. 尽可能使用系统提供的API,因为这些API是编译好的二进制代码,执行效率很高

同时,一些基本的算法上的优化,同样可以用在JavaScript中,比如运算结构的调整,这里就不再赘述了。但是由于JavaScript是解释型的,一般不会在运行时对字节码进行优化,所以这些优化仍然是很重要的。

当然,其实这里的一些技巧同样使用在其他的一些解释型语言中,大家也可以进行参考。

posted @ 2006-07-19 10:13 boddi 阅读(204) | 评论 (0)编辑 收藏

escape() & encodeURI() & encodeURIComponent()

escape() & encodeURI() & encodeURIComponent()这三个函数都可以用来对URI进行encode或过滤特殊字符(#/$&+=?/等)。我的经验是最好用encodeURIComponent()(需要IE 5.5以上,FireFox当然没问题),因为对UTF-8支持比较好,不会遇到中文乱码问题,否则还需要进行编码转换,很麻烦的。Terac Rome就是用的encodeURIComponent(),del.icio.us和365key用的是escape()。当然,有人这三个函数都不用,自己写过滤函数,我觉得那纯粹是浪费精力。

下面是MSDN上对这三个函数的解释:

escape(charString)

The escape method returns a string value (in Unicode format) that contains the contents of charstring. All spaces, punctuation, accented characters, and any other non-ASCII characters are replaced with %xx encoding, where xx is equivalent to the hexadecimal number representing the character. For example, a space is returned as "%20."

Characters with a value greater than 255 are stored using the %uxxxx format.

Note   The escape method should not be used to encode Uniform Resource Identifiers (URI). Use encodeURI and encodeURIComponent methods instead.

http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthescape.asp

 encodeURI(URIString)

The encodeURI method returns an encoded URI. If you pass the result to decodeURI, the original string is returned. The encodeURI method does not encode the following characters: ":", "/", ";", and "?". Use encodeURIComponent to encode these characters.

http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthfencodeuri.asp

encodeURIComponent(encodedURIString)

The encodeURIComponent method returns an encoded URI. If you pass the result to decodeURIComponent, the original string is returned. Because the encodeURIComponent method encodes all characters, be careful if the string represents a path such as /folder1/folder2/default.html. The slash characters will be encoded and will not be valid if sent as a request to a web server. Use the encodeURI method if the string contains more than a single URI component.

http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthencodeuricomponent.asp
encodeURIComponent 方法返回一个已编码的 URI。如果将编码结果传递给 decodeURIComponent,则将返回初始的字符串。因为 encodeURIComponent 方法将对所有字符编码,请注意,如果该字符串代表一个路径,例如 /kyle/blogpost.asp,则其中的斜杠也将被编码,这样,当该字符串作为请求发送到 Web 服务器时它将是无效的。如果字符串中包含多个 URI 组件,请使用 encodeURI 方法进行编码。
【例子】encodeURIComponent('/?&神泥') 返回 %2F%3F%26%E7%A5%9E%E6%B3%A5

encodeURI 方法返回一个已编码的 URI。如果将编码结果传递给 decodeURI,则将返回初始的字符串。encodeURI 不对下列字符进行编码:“:”、“/”、“;”和“?”。请使用 encodeURIComponent 对这些字符进行编码。
【例子】encodeURI('/?&神泥') 返回 /?&%E7%A5%9E%E6%B3%A5

posted @ 2006-07-19 10:00 boddi 阅读(539) | 评论 (0)编辑 收藏

仅列出标题
共3页: 上一页 1 2 3