posts - 195, comments - 34, trackbacks - 0, articles - 1

          最大公約數和最小公倍數

          語言: C, 標簽: 無  2008/07/22發布 5個月前更新 更新記錄
          作者: 半瓶墨水, 點擊5221次, 評論(0), 收藏者(0), , 打分:登錄以后才能打分, 目前平均0.0分,總分0, 共有0個用戶參與打分
          # 以下描述來自: http://baike.baidu.com/view/47637.htm
          #
          # 最大公約數(greatest common divisor,簡寫為gcd;
          # 指某幾個整數共有公約數中的最大一個
          #  例: 在2、4、6中,2就是2,4,6的最大公約數。
          #
          # 重要性質:
          # gcd(a,b)=gcd(b,a) (交換律)
          # gcd(-a,b)=gcd(a,b)
          # gcd(a,a)=|a|
          # gcd(a,0)=|a|
          # gcd(a,1)=1
          # gcd(a,b)=gcd(b, a mod b)
          # gcd(a,b)=gcd(b, a-b)
          # 如果有附加的一個自然數m,
          # 則: gcd(ma,mb)=m * gcd(a,b) (分配率)
          # gcd(a+mb ,b)=gcd(a,b)
          # 如果m是a和b的最大公約數,
          # 則: gcd(a/m ,b/m)=gcd(a,b)/m
          # 在乘法函數中有:
          # gcd(ab,m)=gcd(a,m) * gcd(b,m)
          # 兩個整數的最大公約數主要有兩種尋找方法:
          # * 兩數各分解質因子,然后取出同樣有的項乘起來
          # * 輾轉相除法(擴展版)
          # 和最小公倍數(lcm)的關系:
          # gcd(a, b) * lcm(a, b) = ab
          # a與b有最大公約數,但不一定有最小公倍數。
          # 兩個整數的最大公因子可用于計算兩數的最小公倍數,或分數化簡成最簡分數。
          # 兩個整數的最大公因子和最小公倍數中存在分配律:
          # * gcd(a, lcm(b, c)) = lcm(gcd(a, b), gcd(a, c))
          # * lcm(a, gcd(b, c)) = gcd(lcm(a, b), lcm(a, c))
          # 在坐標里,將點(0, 0)和(a, b)連起來,通過整數坐標的點的數目(除了(0, 0)一點之外)就是gcd(a, b)。
          #
          #
          # 以下代碼來自: http://bbs.bccn.net/thread-224663-1-1.html
          #
          int GCD(int a, int b)
          {
             if(b == 0) return a;
             else return GCD(b, a % b);
          }

          int LCM(int a, int b)
          {
             return a * b / GCD(a,b);
          }

          /*以下代碼來自:http://en.wikipedia.org/wiki/Binary_GCD_algorithm */
          unsigned int gcd(unsigned int u, unsigned int v)
          {
              int shift;

              /* GCD(0,x) := x */
              if (u == 0 || v == 0)
                  return u | v;

              /* Let shift := lg K, where K is the greatest power of 2
                 dividing both u and v. */
              for (shift = 0; ((u | v) & 1) == 0; ++shift) {
                  u >>= 1;
                  v >>= 1;
              }

              while ((u & 1) == 0)
                  u >>= 1;

              /* From here on, u is always odd. */
              do {
                  while ((v & 1) == 0/* Loop X */
                      v >>= 1;

                  /* Now u and v are both odd, so diff(u, v) is even.
                     Let u = min(u, v), v = diff(u, v)/2. */
                  if (u < v) {
                      v -= u;
                  } else {
                      unsigned int diff = u - v;
                      u = v;
                      v = diff;
                  }
                  v >>= 1;
              } while (v != 0);

              return u << shift;
          }

          posted @ 2009-10-26 22:56 小強摩羯座 閱讀(291) | 評論 (0)編輯 收藏

               摘要:     1package dwq.algo.sort;   2   3import java.util.Arrays;   4   5public class Sorting   6{   7 ...  閱讀全文

          posted @ 2009-10-26 11:53 小強摩羯座 閱讀(181) | 評論 (0)編輯 收藏

               摘要: package com.dwq.algo; import java.util.ArrayList; public class LongestIncrementSubarray {     public static void main(String[] arg...  閱讀全文

          posted @ 2009-10-26 11:39 小強摩羯座 閱讀(181) | 評論 (0)編輯 收藏

          召集)你能想到的最奇妙的算法題是什么?
          http://www.matrix67.com/blog/archives/1850

          DLX
          http://sqybi.com/works/dlxcn/


          OI最后的謝幕·18歲新的開始http://blog.sina.com.cn/s/blog_4a443fd701000bko.html

          posted @ 2009-10-26 01:09 小強摩羯座 閱讀(194) | 評論 (0)編輯 收藏

          轉]怎樣做人
          1、不要推卸責任,哪怕是別人的責任。無論發生任何事情,首先想到自己是不是做錯了。如果自己沒錯(那是不可能的),那就站在對方的角度上,體驗一下對方的感受。(本人將此點放在第一條是提醒自己永遠都要帶著責任心去做事)
          2、要讓自己適應環境,而不是讓環境來適應你。哪怕這是一個非常痛苦的過程。新到一個地方不要急于融入到其中的哪一個圈子中去,等到了足夠的時間和考驗,屬于你的圈子自然就會接納你。(本人十幾年跳到過的地方多不勝數,這是一條最寶貴的原則)
          3、大方一點,不會大方就學大方一點。如果大方讓你很心痛,你就裝大方一點。(不怕大家笑話,我最大方)
          4、低調一點,再低調一點,永遠低調一點(要比臨時工還要低調一點,可能在別人的眼光中你還不如一個新來的臨時工呢,本人還沒完全做的到)。
          5、嘴甜且不吝惜自己的喝彩聲,要會夸人,好的夸獎讓人覺得很舒服,但不要過份讓人反感。(呵呵,這點我就不太行了,不過還可以)
          6、如果你覺得最近工作順利的不得了,那你就要更加小心了。(順境思危,本人自信做的還行吧
          7、禮貌對待人,打招呼的時候要看著對方的眼睛。永遠記著自己就是一個不者不扣的小字輩。(做人起碼的原則)
          8、言多必失,少說多做,人多的場合少說話。(本人吃的虧太多了,這個原則是用教訓換來的)
          9、不要把別人的好,當做理所當然,要知道感恩圖報。(中國人的優秀傳統不要忘記了)
          10、手高眼低,要有平常心。(沒有什么大不了的,好事呀往壞處想,壞事要往好處想,塞翁失馬,禍福難測啊)
          11、遵守時間,但不要期待別人同樣遵守時間。(對自己永遠嚴格要求不會錯)
          12、信守諾言,但不要輕易許諾,更不要把別人的對你的承諾記在心里并信以為真。(本人提醒大家就算要承諾也要承諾永遠做不到的,呵呵)
          13、不要向同事借錢,如果借了那就要準時還;不要借錢給同事,如果不得不借,就當作是送給他的好了。(呵呵,別把金錢看的太重,不過沒錢萬萬不能)
          14、如果你帶領一個團隊,在總結工作時要把錯誤攬在自己的身上,把功勞都記在下屬的頭上。當上司和下屬同時在場的時候你要記得及時表揚你的下屬。(批評人的時候一定要在只有你們兩個人的情況下才能進行,保持團隊的凝聚力最重要)
          15、不要在一個同事面前不要說另外一個同事的壞話,要堅持說人的好話,別擔心這好話傳不到對方的耳中。如果有人在你面前說其他人的壞話,你要保持正常的微笑,不參與評論。(流言止于己,禍從口出是至理名言)
          16、避免與同事公開對立,包括公開提出反對意見,激烈的更不可取。(兩虎相爭,必有一傷,堅持具備平衡的做人處事能力就會自然化解反對意見)
          17、經常幫助別人,但是不要讓對方覺得是你理所當然應該做的。(好心有時候不會有好結果,但不能因此而灰心,天長地久見人心,一句話:苦心人,天不誤!)
          18、說實在話會讓你倒足八輩子的霉。(本人吃的虧就是一種財富,這是必須堅持的原則)
          19、做事先做人,對事不對人;對事無情,同時對人要有情。(公平、公正、公開)
          20、經常檢查自己是不是又驕傲了,又自負了,又看不起別人了。(一個人再有天大的本事,如果沒有別人的合作和幫助都是白搭)
          21、忍耐是人生一輩子要修煉的一門功課,要用一輩子的時間去學。(張家名言:張公百忍,另有“百忍堂”為證)
          22、盡量不要和同事發生有什么辦公室戀情,如果實在避免不了的話,那就在辦公室避免任何形式的接觸,包括眼神。(兔子不吃窩邊草,煩惱總是從身邊開始的)
          23、待上以敬,待下以寬。(要會拍上司的馬屁,這是和上司溝通的重要途徑之一,但千萬不要弄臟了手)
          24、資歷非常重要,不要和老家伙們耍心眼斗法,否則你會死的很難看。(中國的社會傳統,不會有錯)

          posted @ 2009-10-25 14:51 小強摩羯座 閱讀(191) | 評論 (0)編輯 收藏

          最長遞增子序列的求法 LIS (轉)

          什么是最長遞增子序列呢?
          問題描述如下:
             設L=<a1,a2,…,an>是n個不同的實數的序列,L的遞增子序列是這樣一個子序列Lin=<aK1,ak2,…,akm>,其中k1<k2<…<km且aK1<ak2<…<akm。求最大的m值。
          對于這個問題有以下幾種解決思路:
             1、把a1,a2,...,an排序,假設得到a'1,a'2,...,a'n,然后求a的a'的最長公共子串,這樣總的時間復雜度為o(nlg(n))+o(n^2)=o(n^2);
             2、動態規劃的思路:
              另設一輔助數組b,定義b[n]表示以a[n]結尾的最長遞增子序列的長度,則狀態轉移方程如下:b[k]=max(max(b[j]|a[j]<a[k],j<k)+1,1);
              這個狀態轉移方程解釋如下:在a[k]前面找到滿足a[j]<a[k]的最大b[j],然后把a[k]接在它的后面,可得到a[k]的最長遞增子序列的長度,或者a[k]前面沒有比它小的a[j],那么這時a[k]自成一序列,長度為1.最后整個數列的最長遞增子序列即為max(b[k]   | 0<=k<=n-1);
              實現代碼如下:
              

          #include <iostream>

          using namespace std;

          int main()

          {

                 int i,j,n,a[100],b[100],max;

                 while(cin>>n)

                 {

                        for(i=0;i<n;i++)

                               cin>>a[i];

                        b[0]=1;//初始化,以a[0]結尾的最長遞增子序列長度為1

                        for(i=1;i<n;i++)

                        {

                               b[i]=1;//b[i]最小值為1

                               for(j=0;j<i;j++)

                                      if(a[i]>a[j]&&b[j]+1>b[i])

                                             b[i]=b[j]+1;

                        }

                        for(max=i=0;i<n;i++)//求出整個數列的最長遞增子序列的長度

                               if(b[i]>max)

                                      max=b[i];

                        cout<<max<<endl;

                 }

                 return 0;

          }

              顯然,這種方法的時間復雜度仍為o(n^2);
             3、對第二種思路的改進:
              第二種思路在狀態轉移時的復雜度為o(n),即在找a[k]前面滿足a[j]<a[k]的最大b[j]時采用的是順序查找的方法,復雜度為o(n).
              設想如果能把順序查找改為折半查找,則狀態轉移時的復雜度為o(lg(n)),這個問題的總的復雜度就可以降到nlg(n).
              另定義一數組c,c中元素滿足c[b[k]]=a[k],解釋一下,即當遞增子序列的長度為b[k]時子序列的末尾元素為c[b[k]]=a[k].
              先給出這種思路的代碼,然后再對其做出解釋。
              

          #include <iostream>

          using namespace std;

          int find(int *a,int len,int n)//若返回值為x,a[x]>=n>a[x-1]

          {

                 int left=0,right=len,mid=(left+right)/2;

                 while(left<=right)

                 {

                        if(n>a[mid]) left=mid+1;

                        else if(n<a[mid]) right=mid-1;

                        else return mid;

                        mid=(left+right)/2;

                 }

                 return left;

          }

          void fill(int *a,int n)

          {

                 for(int i=0;i<=n;i++)

                        a[i]=1000;

          }

          int main()

          {

                 int max,i,j,n,a[100],b[100],c[100];

                 while(cin>>n)

                 {

                        fill(c,n+1);

                        for(i=0;i<n;i++)

                               cin>>a[i];

                        c[0]=-1;//    …………………………………………1

                        c[1]=a[0];//        ……………………………………2

                        b[0]=1;//     …………………………………………3

                        for(i=1;i<n;i++)//        ………………………………4

                        {

                               j=find(c,n+1,a[i]);//   ……………………5

                               c[j]=a[i];// ………………………………6

                               b[i]=j;//……………………………………7

                        }

                        for(max=i=0;i<n;i++)//………………………………8

                               if(b[i]>max)

                                      max=b[i];

                        cout<<max<<endl;

                 }

                 return 0;

          }

              對于這段程序,我們可以用算法導論上的loop invariants來幫助理解.
              loop invariant: 1、每次循環結束后c都是單調遞增的。(這一性質決定了可以用二分查找)
                                     2、每次循環后,c[i]總是保存長度為i的遞增子序列的最末的元素,若長度為i的遞增子序

                                            列有多個,剛保存末尾元素最小的那個.(這一性質決定是第3條性質成立的前提)
                                     3、每次循環完后,b[i]總是保存以a[i]結尾的最長遞增子序列。
              initialization:    1、進入循環之前,c[0]=-1,c[1]=a[0],c的其他元素均為1000,c是單調遞增的;
                                     2、進入循環之前,c[1]=a[0],保存了長度為1時的遞增序列的最末的元素,且此時長度為1

                                           的遞增了序列只有一個,c[1]也是最小的;
                                     3、進入循環之前,b[0]=1,此時以a[0]結尾的最長遞增子序列的長度為1.
              maintenance:   1、若在第n次循環之前c是單調遞增的,則第n次循環時,c的值只在第6行發生變化,而由

                                          c進入循環前單調遞增及find函數的性質可知(見find的注釋),

                                           此時c[j+1]>c[j]>=a[i]>c[j-1],所以把c[j]的值更新為a[i]后,c[j+1]>c[j]>c[j-1]的性質仍然成

                                          立,即c仍然是單調遞增的;
                                     2、循環中,c的值只在第6行發生變化,由c[j]>=a[i]可知,c[j]更新為a[i]后,c[j]的值只會變

                                            小不會變大,因為進入循環前c[j]的值是最小的,則循環中把c[j]更新為更小的a[i],當

                                           然此時c[j]的值仍是最小的;
                                     3、循環中,b[i]的值在第7行發生了變化,因為有loop invariant的性質2,find函數返回值

                                          為j有:c[j-1]<a[i]<=c[j],這說明c[j-1]是小于a[i]的,且以c[j-1]結尾的遞增子序列有最大的

                                         長度,即為j-1,把a[i]接在c[j-1]后可得到以a[i]結尾的最長遞增子序列,長度為(j-1)+1=j;
              termination:       循環完后,i=n-1,b[0],b[1],...,b[n-1]的值均已求出,即以a[0],a[1],...,a[n-1]結尾的最長遞

                                        增子序列的長度均已求出,再通過第8行的循環,即求出了整個數組的最長遞增子序列。

                    仔細分析上面的代碼可以發現,每次循環結束后,假設已經求出c[1],c[2],c[3],...,c[len]的值,則此時最長遞增子序列的長度為len,因此可以把上面的代碼更加簡化,即可以不需要數組b來輔助存儲,第8行的循環也可以省略。
              

          #include <iostream>

          using namespace std;

          int find(int *a,int len,int n)//修改后的二分查找,若返回值為x,則a[x]>=n

          {

                 int left=0,right=len,mid=(left+right)/2;

                 while(left<=right)

                 {

                        if(n>a[mid]) left=mid+1;

                        else if(n<a[mid]) right=mid-1;

                        else return mid;

                        mid=(left+right)/2;

                 }

                 return left;

          }

          int main()

          {

                 int n,a[100],b[100],c[100],i,j,len;//新開一變量len,用來儲存每次循環結束后c中已經求出值的元素的最大下標

                 while(cin>>n)

                 {

                        for(i=0;i<n;i++)

                               cin>>a[i];

                        b[0]=1;

                        c[0]=-1;

                        c[1]=a[0];

                        len=1;//此時只有c[1]求出來,最長遞增子序列的長度為1.

                        for(i=1;i<n;i++)

                        {

                               j=find(c,len,a[i]);

                               c[j]=a[i];

                               if(j>len)//要更新len,另外補充一點:由二分查找可知j只可能比len1

                                      len=j;//更新len

                        }

                        cout<<len<<endl;

                 }

                 return 0;

          }

          最長遞增部分序列 Longest Ordered Subsequence Extention hoj10027 poj2533
          2007-08-21 20:19

          求最長遞增部分序列是一個比較常見的動態規劃題。在導彈攔截等題中都有用到。一般來說就是用經典的O(n^2)的動態規劃算法。

          算法如下:

                   設A[i]表示序列中的第i個數,F[i]表示從1到i這一段中以i結尾的最長上升子序列的長度,初始時設F[i] = 0(i = 1, 2, ..., len(A))。則有動態規劃方程:F[i] = max{1, F[j] + 1} (j = 1, 2, ..., i - 1, 且A[j] < A[i])。

          然而在hoj10027中n的值達到了50000。顯而易見經典算法是會超時滴。所以只有另謀出路了。

                   用一個變量len記錄到目前為止所找出來的最長遞增序列的長度。另外準備一個數組b[],用這個數組表示長度為j的遞增序列中最后一個元素的值。在這里長度為j的遞增序列不止一個,我們所要保存是那個最小的。為什么呢?因為最后一個元素越小,那么這個遞增序列在往后被延長的機會越大。初始化b[0] = -1;len = 0;從第一個元素a[1]開始       a[i]( 1 <= i <= n)。如果這個元素比len長的序列的最大值大。則把這個元素直接添加到b數組的后面。如果這個元素比b數組的第一個元素還要小則把這個元素賦給b數組的第一個值。否則進行二分查找。當在b數組里面找到一個數比a[i]小,并且他的后面的數大于或等于a[i]則跳出。將a[i]添加到這個數的后面。輸出len就可以了。

          代碼如下:

          #include <stdio.h>
          #include <string.h>
          int main()
          {
          int a[50001], b[50001];
          int i, j, l, r, len, n, mid;
          while (scanf("%d", &n) != EOF)
          {
             for (i = 0; i < n; i++)
              scanf("%d", &a[i]);
             len = 0;
             memset(b, 0, sizeof(int) * 50001);
             b[0] = -1;
             for (i = 0; i < n; i++)
             {
              if (a[i] > b[len])
               b[++len] = a[i];
              else if (a[i] < b[1] )
               b[1] = a[i];
              else
              {
               l = 1; r = len;
               while (l <= r)
               {
                mid = (l + r)>>1;
                if (a[i] > b[mid] && a[i] <= b[mid + 1])
                {
                 j = mid;
                 break;
                }
                if (b[mid] > a[i])
                {
                 r = mid - 1;
                }
                else
                {
                 j = mid;
                 l = mid + 1;
                }
               }
               b[j + 1] = a[i];
              }
             }
             printf("%d\n", len);
          }
          return 0;

          posted @ 2009-10-25 11:27 小強摩羯座 閱讀(2925) | 評論 (1)編輯 收藏

           Java把內存劃分成兩種:一種是棧內存,一種是堆內存。

              在函數中定義的一些基本類型的變量和對象的引用變量都在函數的棧內存中分配。

              當在一段代碼塊定義一個變量時,Java就在棧中為這個變量分配內存空間,當超過變量的作用域后,Java會自動釋放掉為該變量所分配的內存空間,該內存空間可以立即被另作他用。

              堆內存用來存放由new創建的對象和數組。

              在堆中分配的內存,由Java虛擬機的自動垃圾回收器來管理。

              在堆中產生了一個數組或對象后,還可以在棧中定義一個特殊的變量,讓棧中這個變量的取值等于數組或對象在堆內存中的首地址,棧中的這個變量就成了數組或對象的引用變量。

              引用變量就相當于是為數組或對象起的一個名稱,以后就可以在程序中使用棧中的引用變量來訪問堆中的數組或對象。

              具體的說:

              棧與堆都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。

              Java的堆是一個運行時數據區,類的(對象從中分配空間。這些對象通過new、newarray、anewarray和multianewarray等 指令建立,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時 動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由于要在運行時動態分配內存,存取速度較慢。

              棧的優勢是,存取速度比堆要快,僅次于寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本 類型的變量(,int, short, long, byte, float, double, boolean, char)和對象句柄。

              棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義:

              int a = 3;

              int b = 3;

              編譯器先處理int a = 3;首先它會在棧中創建一個變量為a的引用,然后查找棧中是否有3這個值,如果沒找到,就將3存放進來,然后將a指向3。接著處理int b = 3;在創建完b的引用變量后,因為在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的情況。這時,如果再令a=4;那么編譯器 會重新搜索棧中是否有4值,如果沒有,則將4存放進來,并令a指向4;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。要注意這 種數據的共享與兩個對象的引用同時指向一個對象的這種共享是不同的,因為這種情況a的修改并不會影響到b, 它是由編譯器完成的,它有利于節省空間。而一個對象引用變量修改了這個對象的內部狀態,會影響到另一個對象引用變量。

              String是一個特殊的包裝類數據。可以用:

              String str = new String("abc");

              String str = "abc";

              兩種的形式來創建,第一種是用new()來新建對象的,它會在存放于堆中。每調用一次就會創建一個新的對象。

              而第二種是先在棧中創建一個對String類的對象引用變量str,然后查找棧中有沒有存放"abc",如果沒有,則將"abc"存放進棧,并令str指向“abc”,如果已經有“abc” 則直接令str指向“abc”。

              比較類里面的數值是否相等時,用equals()方法;當測試兩個包裝類的引用是否指向同一個對象時,用==,下面用例子說明上面的理論。

              String str1 = "abc";

              String str2 = "abc";

              System.out.println(str1==str2); //true可以看出str1和str2是指向同一個對象的。

              String str1 =new String ("abc");

              String str2 =new String ("abc");

              System.out.println(str1==str2); // false用new的方式是生成不同的對象。每一次生成一個。

              因此用第二種方式創建多個“abc”字符串,在內存中其實只存在一個對象而已. 這種寫法有利與節省內存空間. 同時它可以在一定程度上提高程序的運行速度,因為JVM會自動根據棧中數據的實際情況來決定是否有必要創建新對象。而對于String str = new String("abc");的代碼,則一概在堆中創建新對象,而不管其字符串值是否相等,是否有必要創建新對象,從而加重了程序的負擔。

              另一方面, 要注意: 我們在使用諸如String str = "abc";的格式定義類時,總是想當然地認為,創建了String類的對象str。擔心陷阱!對象可能并沒有被創建!而可能只是指向一個先前已經創建的 對象。只有通過new()方法才能保證每次都創建一個新的對象。 由于String類的immutable性質,當String變量需要經常變換其值時,應該考慮使用StringBuffer類,以提高程序效率。

              java中內存分配策略及堆和棧的比較

              2.1 內存分配策略按照編譯原理的觀點,程序運行時的內存分配有三種策略,分別是靜態的,棧式的,和堆式的.靜態存儲分配是指在編譯時就能確定每個數據目標在運行時刻的存儲空間需求,因而在編譯時就可以給他們分配固定的內存空間.這種分配策略要求程序代碼中不允 許有可變數據結構(比如可變數組)的存在,也不允許有嵌套或者遞歸的結構出現,因為它們都會導致編譯程序無法計算準確的存儲空間需求.棧式存儲分配也可稱為動態存儲分配,是由一個類似于堆棧的運行棧來實現的.和靜態存儲分配相反,在棧式存儲方案中,程序對數據區的需求在編譯時是完全未知 的,只有到運行的時候才能夠知道,但是規定在運行中進入一個程序模塊時,必須知道該程序模塊所需的數據區大小才能夠為其分配內存.和我們在數據結構所熟知 的棧一樣,棧式存儲分配按照先進后出的原則進行分配。

              靜態存儲分配要求在編譯時能知道所有變量的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負責在編譯時或運行時 模塊入口處都無法確定存儲要求的數據結構的內存分配,比如可變長度串和對象實例.堆由大片的可利用塊或空閑塊組成,堆中的內存可以按照任意順序分配和釋 放.

              2.2 堆和棧的比較

              上面的定義從編譯原理的教材中總結而來,除靜態存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態存儲分配,集中比較堆和棧:從堆和棧的功能和作用來通俗的比較,堆主要用來存放對象的,棧主要是用來執行程序的.而這種不同又主要是由于堆和棧的特點決定的:在編程中,例如C/C++中,所有的方法調用都是通過棧來進行的,所有的局部變量,形式參數都是從棧中分配內存空間的。實際上也不是什么分配,只是從棧頂 向上用就行,就好像工廠中的傳送帶(conveyor belt)一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數的時候,修改棧指針就可以把棧中的內容銷毀.這樣的模式速度最快, 當然要用來運行程序了.需要注意的是,在分配的時候,比如為一個即將要調用的程序模塊分配數據區時,應事先知道這個數據區的大小,也就說是雖然分配是在程 序運行時進行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時確定的,不是在運行時.堆是應用程序在運行的時候請求操作系統分配給自己內存,由于從操作系統管理的內存分配,所以在分配和銷毀時都要占用時間,因此用堆的效率非常低.但是堆的 優點在于,編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數據要在堆里停留多長的時間,因此,用堆保存數據時會得到更大的靈活性。事實上,面 向對象的多態性,堆內存分配是必不可少的,因為多態變量所需的存儲空間只有在運行時創建了對象之后才能確定.在C++中,要求創建一個對象時,只需用 new命令編制相關的代碼即可。執行這些代碼時,會在堆里自動進行數據的保存.當然,為達到這種靈活性,必然會付出一定的代價:在堆里分配存儲空間時會花 掉更長的時間!這也正是導致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優點往往也是人的缺點,人的缺點往往也是人的優點.

              2.3 JVM中的堆和棧JVM是基于堆棧的虛擬機.JVM為每個新創建的線程都分配一個堆棧.也就是說,對于一個Java程序來說,它的運行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態。JVM對堆棧只進行兩種操作:以幀為單位的壓棧和出棧操作。

              我們知道,某個線程正在執行的方法稱為此線程的當前方法.我們可能不知道,當前方法使用的幀稱為當前幀。當線程激活一個Java方法,JVM就會在線程的 Java堆棧里新壓入一個幀。這個幀自然成為了當前幀.在此方法執行期間,這個幀將用來保存參數,局部變量,中間計算過程和其他數據.這個幀在這里和編譯 原理中的活動紀錄的概念是差不多的.從Java的這種分配機制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統在建立某個進程時或者線程(在支持多線程的操作系統中是線程)為這個線程建立的存儲區域,該區域具有先進后出的特性。

              每一個Java應用都唯一對應一個JVM實例,每一個實例唯一對應一個堆。應用程序在運行中所創建的所有類實例或數組都放在這個堆中,并由應用所有的線程 共享.跟C/C++不同,Java中分配堆內存是自動初始化的。Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在堆棧中分配,也 就是說在建立一個對象時從兩個地方都分配內存,在堆中分配的內存實際建立這個對象,而在堆棧中分配的內存只是一個指向這個堆對象的指針(引用)而已。

          posted @ 2009-10-12 21:22 小強摩羯座 閱讀(188) | 評論 (0)編輯 收藏


          在《編程珠璣》中有詳細的討論。主要出于性能方向改進。

          1. 二分法很簡單吧 ,但要想 一次寫對  也不容易吧 ,更何況他的一些擴展應用呢 ,我這里擴展了四種,<P> </P><P>基礎知識 還是牢靠的好</P><P> </P>  
          1. /**  
          2.  * Author: yiminghe  
          3.  * Date: 2008-10-13  
          4.  * Time: 23:50:48  
          5.  * Any problem ,contact yiminghe@fudan.edu.cn.  
          6.  */  
          7. public class BinarySearch {   
          8.   
          9.     //返回中間一個數   
          10.     //12345666689   
          11.     // 6  不確定返回哪個6   
          12.     public static int b1(int[] array, int v) {   
          13.         int left = 0;   
          14.         int right = array.length - 1;   
          15.         while (left <= right) {   
          16.             int middle = (left + right) / 2;   
          17.             if (array[middle] == v) return middle;   
          18.             if (array[middle] > v)   
          19.                 right = middle - 1;   
          20.             else  
          21.                 left = middle + 1;   
          22.         }   
          23.   
          24.         return -1;   
          25.   
          26.     }   
          27.   
          28.     //返回重復元素的最后一個數   
          29.     //123456667   
          30.     //最后一個6位置返回    
          31.     public static int b2(int[] array, int v) {   
          32.         int left = 0;   
          33.         int right = array.length - 1;   
          34.         while (left < right) {   
          35.             int middle = (left + right + 1) / 2;   
          36.             if (array[middle] > v)   
          37.                 right = middle - 1;   
          38.             else  
          39.                 left = middle;   
          40.         }   
          41.   
          42.         if (array[left] == v)   
          43.             return left;   
          44.   
          45.         return -1;   
          46.   
          47.     }   
          48.   
          49.   
          50.     //返回重復元素的最前一個數   
          51.     //123456667   
          52.     //最前一個6位置返回   
          53.     public static int b3(int[] array, int v) {   
          54.         int left = 0;   
          55.         int right = array.length - 1;   
          56.         while (left < right) {   
          57.             int middle = (left + right) / 2;   
          58.             if (array[middle] < v)   
          59.                 left = middle + 1;   
          60.             else  
          61.                 right = middle;   
          62.         }   
          63.   
          64.         if (array[right] == v)   
          65.             return right;   
          66.   
          67.         return -1;   
          68.   
          69.     }   
          70.   
          71.   
          72.     //返回重復元素的最前一個數   
          73.     //1234566689   
          74.     //最前一個6位置返回 ,若找不到,顯示 比他小的離它最大位置,比它小的離它最小位置   
          75.     //如 找 7 ,則 輸出 最后一個6的位置 和 8 的位置   
          76.     public static int b4(int[] array, int v, int flag) {   
          77.         int left = 0;   
          78.         int right = array.length - 1;   
          79.         while (left < right) {   
          80.             int middle = (left + right) / 2;   
          81.             if (array[middle] < v)   
          82.                 left = middle + 1;   
          83.             else  
          84.                 right = middle;   
          85.         }   
          86.   
          87.   
          88.         if (array[right] == v)   
          89.             return right;   
          90.         System.out.println(right - 1 + "  -- " + left);   
          91.         return -1;   
          92.   
          93.     }   
          94.   
          95.   
          96.     public static void main(String[] args) {   
          97.         //                       0, 1, 2, 3  4  5  6  7   
          98.         int[] array = new int[]{12341016161616161618110};   
          99.         //array = new int[]{0, 6};   
          100.         //array = new int[]{6, 7};   
          101.         System.out.println(b1(array, 16));   
          102.         System.out.println(b2(array, 16));   
          103.         System.out.println(b3(array, 16));   
          104.         System.out.println(b4(array, 61));   
          105.   
          106.   
          107.     }   
          108. }  

           


          posted @ 2009-09-30 23:47 小強摩羯座 閱讀(87) | 評論 (0)編輯 收藏

          Java AIO初探(異步網絡IO) 

            按照《Unix網絡編程》的劃分,IO模型可以分為:阻塞IO、非阻塞IO、IO復用、信號驅動IO和異步IO,按照POSIX標準來劃分只分為兩類:同步IO和異步IO.如何區分呢?首先一個IO操作其實分成了兩個步驟:發起IO請求和實際的IO操作,同步IO和異步IO的區別就在于第二個步驟是否阻塞,如果實際的IO讀寫阻塞請求進程,那么就是同步IO,因此阻塞IO、非阻塞IO、IO服用、信號驅動IO都是同步IO,如果不阻塞,而是操作系統幫你做完IO操作再將結果返回給你,那么就是異步IO.阻塞IO和非阻塞IO的區別在于第一步,發起IO請求是否會被阻塞,如果阻塞直到完成那么就是傳統的阻塞IO,如果不阻塞,那么就是非阻塞IO.

              Java nio 2.0的主要改進就是引入了異步IO(包括文件和網絡),這里主要介紹下異步網絡IO API的使用以及框架的設計,以TCP服務端為例。首先看下為了支持AIO引入的新的類和接口:

              java.nio.channels.AsynchronousChannel標記一個channel支持異步IO操作。

              java.nio.channels.AsynchronousServerSocketChannel ServerSocket的aio版本,創建TCP服務端,綁定地址,監聽端口等。

              java.nio.channels.AsynchronousSocketChannel面向流的異步socket channel,表示一個連接。

              java.nio.channels.AsynchronousChannelGroup異步channel的分組管理,目的是為了資源共享。一個AsynchronousChannelGroup綁定一個線程池,這個線程池執行兩個任務:處理IO事件和派發CompletionHandler.AsynchronousServerSocketChannel創建的時候可以傳入一個AsynchronousChannelGroup,那么通過AsynchronousServerSocketChannel創建的AsynchronousSocketChannel將同屬于一個組,共享資源。

              java.nio.channels.CompletionHandler異步IO操作結果的回調接口,用于定義在IO操作完成后所作的回調工作。AIO的API允許兩種方式來處理異步操作的結果:返回的Future模式或者注冊CompletionHandler,我更推薦用CompletionHandler的方式,這些handler的調用是由AsynchronousChannelGroup的線程池派發的。顯然,線程池的大小是性能的關鍵因素。AsynchronousChannelGroup允許綁定不同的線程池,通過三個靜態方法來創建:public static AsynchronousChannelGroup withFixedThreadPool(int nThreads,

              ThreadFactory threadFactory)

              throws IOException

              public static AsynchronousChannelGroup withCachedThreadPool(ExecutorService executor,

              int initialSize)

              public static AsynchronousChannelGroup withThreadPool(ExecutorService executor)

              throws IOException

              需要根據具體應用相應調整,從框架角度出發,需要暴露這樣的配置選項給用戶。

              在介紹完了aio引入的TCP的主要接口和類之后,我們來設想下一個aio框架應該怎么設計。參考非阻塞nio框架的設計,一般都是采用Reactor模式,Reacot負責事件的注冊、select、事件的派發;相應地,異步IO有個Proactor模式,Proactor負責CompletionHandler的派發,查看一個典型的IO寫操作的流程來看兩者的區別:

              Reactor:  send(msg) -> 消息隊列是否為空,如果為空  -> 向Reactor注冊OP_WRITE,然后返回 -> Reactor select -> 觸發Writable,通知用戶線程去處理 ->先注銷Writable(很多人遇到的cpu 100%的問題就在于沒有注銷),處理Writeable,如果沒有完全寫入,繼續注冊OP_WRITE.注意到,寫入的工作還是用戶線程在處理。

              Proactor: send(msg) -> 消息隊列是否為空,如果為空,發起read異步調用,并注冊CompletionHandler,然后返回。 -> 操作系統負責將你的消息寫入,并返回結果(寫入的字節數)給Proactor -> Proactor派發CompletionHandler.可見,寫入的工作是操作系統在處理,無需用戶線程參與。事實上在aio的API中,AsynchronousChannelGroup就扮演了Proactor的角色。

              CompletionHandler有三個方法,分別對應于處理成功、失敗、被取消(通過返回的Future)情況下的回調處理:

              public interface CompletionHandler {

              void completed(V result, A attachment);

              void failed(Throwable exc, A attachment);

              void cancelled(A attachment);

              }

              其中的泛型參數V表示IO調用的結果,而A是發起調用時傳入的attchment.

              在初步介紹完aio引入的類和接口后,我們看看一個典型的tcp服務端是怎么啟動的,怎么接受連接并處理讀和寫,這里引用的代碼都是yanf4j 的aio分支中的代碼,可以從svn checkout,svn地址: http://yanf4j.googlecode.com/svn/branches/yanf4j-aio

              第一步,創建一個AsynchronousServerSocketChannel,創建之前先創建一個AsynchronousChannelGroup,上文提到AsynchronousServerSocketChannel可以綁定一個AsynchronousChannelGroup,那么通過這個AsynchronousServerSocketChannel建立的連接都將同屬于一個AsynchronousChannelGroup并共享資源:this.asynchronousChannelGroup = AsynchronousChannelGroup。withCachedThreadPool(Executors.newCachedThreadPool(),this.threadPoolSize);然后初始化一個AsynchronousServerSocketChannel,通過open方法:this.serverSocketChannel = AsynchronousServerSocketChannel。open(this.asynchronousChannelGroup);通過nio 2.0引入的SocketOption類設置一些TCP選項:this.serverSocketChannel。setOption(StandardSocketOption.SO_REUSEADDR,true);this.serverSocketChannel。setOption(StandardSocketOption.SO_RCVBUF,16*1024);

              綁定本地地址:

              this.serverSocketChannel。bind(new InetSocketAddress("localhost",8080), 100);其中的100用于指定等待連接的隊列大小(backlog)。完了嗎?還沒有,最重要的監聽工作還沒開始,監聽端口是為了等待連接上來以便accept產生一個AsynchronousSocketChannel來表示一個新建立的連接,因此需要發起一個accept調用,調用是異步的,操作系統將在連接建立后,將最后的結果——AsynchronousSocketChannel返回給你:

              public void pendingAccept(){

              if (this.started  this.serverSocketChannel.isOpen()) { this.acceptFuture = this.serverSocketChannel.accept(null,

              new AcceptCompletionHandler());

              } else {

              throw new IllegalStateException("Controller has been closed");

              }

              注意,重復的accept調用將會拋出PendingAcceptException,后文提到的read和write也是如此。accept方法的第一個參數是你想傳給CompletionHandler的attchment,第二個參數就是注冊的用于回調的CompletionHandler,最后返回結果Future.你可以對future做處理,這里采用更推薦的方式就是注冊一個CompletionHandler.那么accept的CompletionHandler中做些什么工作呢?顯然一個赤裸裸的AsynchronousSocketChannel是不夠的,我們需要將它封裝成session,一個session表示一個連接(mina里就叫IoSession了),里面帶了一個緩沖的消息隊列以及一些其他資源等。在連接建立后,除非你的服務器只準備接受一個連接,不然你需要在后面繼續調用pendingAccept來發起另一個accept請求:

              private final class AcceptCompletionHandler implements

              CompletionHandler {

              @Override

              public void cancelled(Object attachment){

              logger.warn("Accept operation was canceled");

              }

            @Override

              public void completed(AsynchronousSocketChannel socketChannel,

              Object attachment){

              try {

              logger.debug("Accept connection from " + socketChannel.getRemoteAddress());

              configureChannel(socketChannel);

              AioSessionConfig sessionConfig = buildSessionConfig(socketChannel);

              Session session = new AioTCPSession(sessionConfig,AioTCPController.this.configuration。getSessionReadBufferSize(),AioTCPController.this.sessionTimeout);session.start();

              registerSession(session);

              } catch(Exception e){

              e.printStackTrace();logger.error("Accept error", e);

              notifyException(e);

              } finally {

              pendingAccept();

              }

              @Override

              public void failed(Throwable exc, Object attachment) { logger.error("Accept error", exc);

              try {

              notifyException(exc);

              } finally {

              pendingAccept();

              }

              注意到了吧,我們在failed和completed方法中在最后都調用了pendingAccept來繼續發起accept調用,等待新的連接上來。有的同學可能要說了,這樣搞是不是遞歸調用,會不會堆棧溢出?實際上不會,因為發起accept調用的線程與CompletionHandler回調的線程并非同一個,不是一個上下文中,兩者之間沒有耦合關系。要注意到,CompletionHandler的回調共用的是AsynchronousChannelGroup綁定的線程池,因此千萬別在回調方法中調用阻塞或者長時間的操作,例如sleep,回調方法最好能支持超時,防止線程池耗盡。

              連接建立后,怎么讀和寫呢?回憶下在nonblocking nio框架中,連接建立后的第一件事是干什么?注冊OP_READ事件等待socket可讀。異步IO也同樣如此,連接建立后馬上發起一個異步read調用,等待socket可讀,這個是Session.start方法中所做的事情:

              public class AioTCPSession {

              protected void start0(){

              pendingRead();

              }

              protected final void pendingRead(){

              if (!isClosed()  this.asynchronousSocketChannel.isOpen()) { if (!this.readBuffer.hasRemaining()) { this.readBuffer = ByteBufferUtils。increaseBufferCapatity(this.readBuffer);

              }

              this.readFuture = this.asynchronousSocketChannel.read(this.readBuffer, this, this.readCompletionHandler);

              } else {

              throw new IllegalStateException(

              "Session Or Channel has been closed");

              }

              }

              AsynchronousSocketChannel的read調用與AsynchronousServerSocketChannel的accept調用類似,同樣是非阻塞的,返回結果也是一個Future,但是寫的結果是整數,表示寫入了多少字節,因此read調用返回的是Future,方法的第一個參數是讀的緩沖區,操作系統將IO讀到數據拷貝到這個緩沖區,第二個參數是傳遞給CompletionHandler的attchment,第三個參數就是注冊的用于回調的CompletionHandler.這里保存了read的結果Future,這是為了在關閉連接的時候能夠主動取消調用,accept也是如此。現在可以看看read的CompletionHandler的實現:

              public final class ReadCompletionHandler implements

              CompletionHandler {

              private static final Logger log = LoggerFactory

              。getLogger(ReadCompletionHandler.class);

              protected final AioTCPController controller;

              public ReadCompletionHandler(AioTCPController controller){

              this.controller = controller;

              }

              @Override

              public void cancelled(AbstractAioSession session){

              log.warn("Session(" + session.getRemoteSocketAddress()

              + ")read operation was canceled");

              }

              @Override

              public void completed(Integer result, AbstractAioSession session) { if (log.isDebugEnabled())

              log.debug("Session(" + session.getRemoteSocketAddress()

              + ")read +" + result + " bytes");

              if(result 0){

              session.updateTimeStamp();session.getReadBuffer()。flip();session.decode();session.getReadBuffer()。compact();

              }

              } finally {

              try {

              session.pendingRead();

              } catch(IOException e){

              session.onException(e);session.close();

              }

              controller.checkSessionTimeout();

              }

              @Override

              public void failed(Throwable exc, AbstractAioSession session) { log.error("Session read error", exc);session.onException(exc);session.close();

              }

              }

           如果IO讀失敗,會返回失敗產生的異常,這種情況下我們就主動關閉連接,通過session.close()方法,這個方法干了兩件事情:關閉channel和取消read調用:if (null != this.readFuture) { this.readFuture.cancel(true);

              }

              this.asynchronousSocketChannel.close();   在讀成功的情況下,我們還需要判斷結果result是否小于0,如果小于0就表示對端關閉了,這種情況下我們也主動關閉連接并返回。如果讀到一定字節,也就是result大于0的情況下,我們就嘗試從讀緩沖區中decode出消息,并派發給業務處理器的回調方法,最終通過pendingRead繼續發起read調用等待socket的下一次可讀。可見,我們并不需要自己去調用channel來進行IO讀,而是操作系統幫你直接讀到了緩沖區,然后給你一個結果表示讀入了多少字節,你處理這個結果即可。而nonblocking IO框架中,是reactor通知用戶線程socket可讀了,然后用戶線程自己去調用read進行實際讀操作。這里還有個需要注意的地方,就是decode出來的消息的派發給業務處理器工作最好交給一個線程池來處理,避免阻塞group綁定的線程池。

              IO寫的操作與此類似,不過通常寫的話我們會在session中關聯一個緩沖隊列來處理,沒有完全寫入或者等待寫入的消息都存放在隊列中,隊列為空的情況下發起write調用:

              protected void write0(WriteMessage message){

              boolean needWrite = false;

              synchronized (this.writeQueue) { needWrite = this.writeQueue.isEmpty();this.writeQueue.offer(message);

              }

              if(needWrite){

              pendingWrite(message);

              }

              protected final void pendingWrite(WriteMessage message){

              message = preprocessWriteMessage(message);

              if (!isClosed()  this.asynchronousSocketChannel.isOpen()) { this.asynchronousSocketChannel.write(message.getWriteBuffer(),this, this.writeCompletionHandler);

              } else {

              throw new IllegalStateException(

              "Session Or Channel has been closed");

              }

              write調用返回的結果與read一樣是一個Future,而write的CompletionHandler處理的核心邏輯大概是這樣:

              @Override

              public void completed(Integer result, AbstractAioSession session) { if (log.isDebugEnabled())

              log.debug("Session(" + session.getRemoteSocketAddress()

              + ")writen " + result + " bytes");

              WriteMessage writeMessage;

              Queue writeQueue = session.getWriteQueue();

              synchronized(writeQueue){

              writeMessage = writeQueue.peek();if (writeMessage.getWriteBuffer() == null || !writeMessage.getWriteBuffer()。hasRemaining()) { writeQueue.remove();if (writeMessage.getWriteFuture() != null) { writeMessage.getWriteFuture()。setResult(Boolean.TRUE);

              }

              try {

              session.getHandler()。onMessageSent(session,writeMessage.getMessage());

              } catch(Exception e){

              session.onException(e);

              }

              writeMessage = writeQueue.peek();

              }

              if (writeMessage != null) {

              try {

              session.pendingWrite(writeMessage);

              } catch(IOException e){

              session.onException(e);session.close();

              }

              compete方法中的result就是實際寫入的字節數,然后我們判斷消息的緩沖區是否還有剩余,如果沒有就將消息從隊列中移除,如果隊列中還有消息,那么繼續發起write調用。

              重復一下,這里引用的代碼都是yanf4j aio分支中的源碼,感興趣的朋友可以直接check out出來看看: http://yanf4j.googlecode.com/svn/branches/yanf4j-aio.在引入了aio之后,java對于網絡層的支持已經非常完善,該有的都有了,java也已經成為服務器開發的首選語言之一。java的弱項在于對內存的管理上,由于這一切都交給了GC,因此在高性能的網絡服務器上還是Cpp的天下。java這種單一堆模型比之erlang的進程內堆模型還是有差距,很難做到高效的垃圾回收和細粒度的內存管理。

              這里僅僅是介紹了aio開發的核心流程,對于一個網絡框架來說,還需要考慮超時的處理、緩沖buffer的處理、業務層和網絡層的切分、可擴展性、性能的可調性以及一定的通用性要求。

          posted @ 2009-09-21 22:22 小強摩羯座 閱讀(283) | 評論 (0)編輯 收藏


          generic points to List<String> , List is called raw type here, and <String> is the parametered type.

          Generic's functions are :
          1. remove type cast;
          2. typesafe programming;

          The key question of Generic:
          1. the inheritance attribute of OO is not support: List<String> is not a type of List<Object>。

          2. Collection<?> c: the parametered type is unknown. so you cant add elem into c.but you may get the elem in c and we know they are of type Object.

          3、類型推斷

          static  < T >   void  fromArrayToCollection(T[] a, Collection  <  T  >  c)   

             
          for  (T o : a)   
                   c.add(o);  
          //  correct  
                }
           
           }

            對這個方法參數化類型T,由定義數據a和Collection時傳入的共同決定,它們可以使用相同類型,也可以使用不同類型,當類型不同時,它們必須有繼承關系,類型推斷時使用較高的那個。
          4, when to use <?> or <T> :他們主要用在泛型方法,T相比?,它有名稱,可多次使用來表達依賴關系。所以在不表達依賴關系的地方就應該使用?。它簡單、靈活。

          而且有趣的是,它們并非水火不容,反而可以精妙配合,如下:  

           

          1  class Collections 
          2
          3  public static < T > void copy(List < T > dest, List < ? extends T > src) {  } 
          4
          5}
           

           

          這個合作使得dest src 的依賴關系得以表達,同時讓 src 的接納范疇擴大了。假如我們只用泛型方法來實現:  

           

          1  class Collections 
          2
          3   public static < T, S extends T > void copy(List < T > dest, List < S > src) {  } 
          4
          5}
           

           

          那么S 的存在就顯得有些不必要,有些不優雅。總的來說,通配符更簡潔清晰,只要情況允許就應該首選。

          5、由于普通數組是協變的,而GenricType由于List<String>不是List<Object>的子類型,所以它不能協變。可以使用?來new 一個數組。但是通常new完就要加元素,而?類型未知不可以產生對象的。所以最后使用Class<T> 類型做形參,實參用T.class就可以。這就是為什么Hibernate.load和get中會使用的.class的用法。比較新知識點是String.class就是Class<String>(String.class稱字面類常量)。當傳入Class<T> c 對象后,可以利用reflect來構造對象,并作狀態設置。


          posted @ 2009-08-15 16:15 小強摩羯座 閱讀(264) | 評論 (0)編輯 收藏

          僅列出標題
          共20頁: First 上一頁 2 3 4 5 6 7 8 9 10 下一頁 Last 
          主站蜘蛛池模板: 文昌市| 南平市| 罗山县| 嘉鱼县| 封丘县| 年辖:市辖区| 通海县| 米泉市| 刚察县| 余庆县| 柳州市| 景德镇市| 化州市| 瑞金市| 克东县| 盖州市| 施甸县| 仙桃市| 彩票| 阿图什市| 雷波县| 古交市| 江山市| 容城县| 江口县| 麻阳| 德庆县| 香格里拉县| 蒙阴县| 洛扎县| 阳原县| 霞浦县| 犍为县| 龙陵县| 枣强县| 天祝| 明星| 武功县| 九寨沟县| 剑阁县| 汶上县|