2012年10月19日 星期五

String Pool

在 Java 所定義的 String 物件裡,可以用兩種方式去指定物件變數的值,例如:

String str1= new String("Java");
String str2= "Java";
由於 str1 與 str2 是兩個不同的物件,當然他的記憶體位址也會不同。所以當我用比較運算子 (==)來比較 str1 與 str2 時,因為比較運算子是比較記憶體位址,可以如期得到 false 的結果。
str1 == str2 /* 回傳 false */
可是,當我另外宣告一個如同 str2 的變數,再去比較一次:
String str2= "Java";
String str3= "Java";

str2 == str3 /* 回傳 true */
這 str2 與 str3 不是兩個不同物件嗎? 為何用比較運算子去比較時,竟然會出現相等的結果?原來,這裡面還有一小段故事啊!在 「JAVA SE6 全方位學習」一書有提到:

Java 為了加快程式的執行速度,又因為字串是每個程式中一定會用到的物件,所以 Java 設計了兩種不同的方式來產生字串物件,一種是呼叫 String 類別的建構子,另一種是使用雙引號【"】。這兩種方式產生出來的字串物件,它們在記憶體中存放的方式不一樣;使用建構子的方式所產生出來的物件,自己有自己的獨立空間,而之所以會有雙引號的方式,主要是為了加快程式執行的速度,所以 Java 會把這類的字串放在一個字串池(String Pool)之中,當用雙引號產生字串物件時,電腦會先去字串池中尋找有沒有相同的字串已經放在裡面了,如果有就直接拿出來用,如果沒有就產生一個新的字串放到字串池中。

當我們看到 Java 為了程式效能的提升,在 String 物件做了如上的調整,那 .Net 陣營的 C# 呢?答案是肯定的。

我把剛剛在 Java 的程式移植到 .Net 平台,改成如下:
String str1=new String("ASP.Net".ToCharArray());
String str2 = "ASP.Net";
String str3 = "ASP.Net";

Console.WriteLine(string.Format("str1==str2 {0}", (str1==str2).ToString()));
/* 回傳 True */
Console.WriteLine(string.Format("str2==str3 {0}", (str2 == str3).ToString()));
/* 回傳 True */

有了先前在 Java 的 String Pool 概念後, str2==str3 回傳 True 已經可以理解,但為何 str1==str2 卻是 True 呢?比較運算子不是去比記憶體位址的嗎?

這當中的曲折,又要用另一個故事來說明了:

參考了「C# - Equals和等於等於( 等於比較運算式 )」這篇文章所引述 MSDN 的一段話:

對於預先定義的實值型別 (Value Type),等號比較運算子 (==) 在運算元相等時傳回 true;否則傳回 false。 對於 string 以外的參考型別,若兩個運算元參考到同一物件,== 會傳回 true。 對於 string 型別,== 會比較字串的值。

原來在 C# ,如果是屬於字串型別,比較運算子會去比較存放在記憶體裡面的值而不是記憶體位址,這一點的確是還蠻細的。

換言之,如果把剛剛 C# 的程式,把原先的字串型別都 Boxing 成 Object 型別呢?

Object obj1 = new String("ASP.Net".ToCharArray());
Object obj2 = "ASP.Net";
Object obj3 = "ASP.Net";

Console.WriteLine(string.Format("obj1==obj2 {0}", (obj1 == obj2).ToString()));
/* 回傳 False */
Console.WriteLine(string.Format("obj2==obj3 {0}", (obj2 == obj3).ToString()));
/* 回傳 True */
改成用 Object 型別後,的確就跟 Java 所認知的概念是一樣了。

最後自己還留下個疑問,到底這樣大費周章的使用 String Pool ,到底可以佔到多少便宜啊?於是寫了兩個小程式比較一下,就讓他跑個 2千萬次的迴圈看看:

Java:
public class jstring02
{
    public static void main(String[] args)
 { 
  System.out.println(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date())); 
  
  String [] a_str= new String[20000000];
  for(int i=0;i<20000000;i++)
  {
   a_str[i]=new String("ASP.Net");
  }
  
  System.out.println(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date())); 

  String [] b_str= new String[20000000];
  for(int i=0;i<20000000;i++)
  {
   b_str[i]="ASP.Net";
  }
    
  System.out.println(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date())); 

 }

}
C#
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(string.Format("{0}", DateTime.Now.ToString()));

        String[] a_str = new String[20000000];
        for (int i = 0; i < 20000000; i++)
        {
            a_str[i] = new String("ASP.Net".ToCharArray());
        }

        Console.WriteLine(string.Format("{0}", DateTime.Now.ToString()));

        String[] b_str = new String[20000000];
        for (int i = 0; i < 20000000; i++)
        {
            b_str[i] = "ASP.Net";
        }
        Console.WriteLine(string.Format("{0}", DateTime.Now.ToString()));

        Console.ReadLine();
    }
}


在 Java 與 C# ,第一次的時間是起始時間,第二次是執行使用建構子方式後的時間,第三次則是執行完 String Pool 後的時間。有了數據上的比較,對於有沒有使用 String Pool 的效能差別,會比較有感覺些。


PS:自己程式功力很差,但主要重點是想凸顯是否有使用 String Pool 的感覺。


 參考:
 01:Java SE 6全方位學習
 02:C# - Equals和等於等於( 等於比較運算式 )

2012年10月4日 星期四

colorbox 的右邊、下面都消失了

今天遇到一個很沒良心的問題,找到問題的癥結點後,反而沒有一種喜悅感,卻隱約有一股想要去撞牆的衝動。

我想要透過 colorbox 去完成一個開視窗的動作,但開的卻很不漂亮。如下圖所示,右邊的框、下面的框都不見了,檢查了程式,該有的都有了,不該有的也加了一堆,畫面就一直缺了一角,醜醜的。


最後看到「Colorbox的官方介绍和colorbox的常见问题中文翻译」提到,jQuery 和 colorbox.css 必須在 jquery.colorbox.js 之前引入。自己檢查了一下程式碼的順序,我竟然把 colorbox.css 擺在最下面。經過調整順序之後,真的恢復正常了。


這烏龍讓我想起一句話:「我們都沒有錯,只是在錯的時間遇上對的人」。

如果硬是要多寫一些話來描述,我覺得引用下面這段還蠻適合的:


當我倆敞開自己
你把自己給我, 我把自己給妳
當我們沉潛
妳進入我, 我進入妳
當我們消失
你消失在我裡面, 我消失在妳裡面
然後, 我是我
而妳是妳!

出處:為愛朗讀

在愛情的世界裡,錯誤的時間遇到對的人,往往不會有好的結局
在程式的世界了,錯誤的順序遇到colorbox,往往不會有好的畫面


參考:
01:Colorbox的官方介绍和colorbox的常见问题中文翻译
02:為愛朗讀