日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

《Java特种兵》1.3 简单数字游戏玩一玩

發布時間:2025/4/16 java 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《Java特种兵》1.3 简单数字游戏玩一玩 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.3 簡單數字游戲玩一玩

數字游戲沒錯就是玩數字游戲

Java怎么玩馬上見證下

玩數字有什么用途呢我們不是虛擬數據給別人看而是通過玩數字轉換讓我們更了解計算機的數字運算也許數字運算可以有一些神奇的地方有些變態的問題也不是我們想的那么簡單。

這里不講基本的“四則運算”胖哥會講一些運算符然后再講講“大數字”是如何處理的。

1.3.1 變量A、B交換有幾種方式

胖哥認為有3種方法來實現變量交換其中一種最簡單的方法就是定義一個變量C作為中間量來實現代碼例子如下

int C = A;

A = B;

B = C;

有人開始不想用“定義一個變量C作為中間量”來實現而是嘗試用“數據疊加后再減回來”的方法

A = A + B;

B = A – B;

A = A – B;

這樣也可以實現A首先變成A與B的和然后減掉B自然就得到A的值再賦值給B此時B存儲了A原先的值再用A減掉B就得到原來B的值了。不過這個方法有一個問題就是A + B這個操作有可能會越界越界后就會出現問題。

既然可能越界聰明的小伙伴們又想到了另一個辦法來解決這個問題那就是“異或”運算

A = A ^ B;

B = A ^ B;

A = A ^ B;

異或運算是按照二進制位進行“異或”的對于A、B兩個數字如果某個二進制位上的A、B都是0或都是1則返回0如果一個是0一個是1則返回1這樣可以得到一個新的數字。這有什么用呢當這個數字再與A發生“異或”的時候就能得到B這個大家可以自行反推。這樣的技巧其實是二進制位上的一個加法但不進位的做法由于只有0、1所以1+1=2后還是為0但是這個運算是最低級的CPU位運算所以它的效率是極高的而且不會越界。

“異或”運算在計算機系統中大量存在它的一個很大的優勢就是可以按照反向的規則還原數據本身。

1.3.2 將無序數據Hash到指定的板塊

假如有一個長度為5000的數組每個數組下標就像一個Hash桶那樣存放一個鏈表此時有一個數據A需要寫入隨機嗎肯定不行因為我們還要查詢數據。那么將它放在Hash桶里面可以嗎似乎可以下面一起來探討一下。

我們希望寫入的數據能按照某種規則讀取出來那么數據應當與數組的下標產生某種關系這樣寫入和讀取只要按照相同的規則就可以達到目的。此時假設一個“非負數”要寫入或許可以這樣做將這個數據%5000就可以得到下標了這樣在數字不斷變化時得到的下標也會不斷變化數據自然會相對均勻分布到5000個數組中當然有特殊情況這里不做探討。如果是負數則需要采用取絕對值Math.abs(A) % 5000來得到下標。

還有沒有其他的方法呢單純要將數據寫入到數組中可以使用A &4999來完成這個操作得到的數據將會永遠<=4999因為它自身二進制位為0的部分&操作后都會被清除為0。理論上可以實現我們的要求而且這種位操作也是最低級的系統運算效率也是極高的。

只是這樣的方法會有一個問題什么問題呢此時我們用數字11來說明會更好一些如果數組的長度是11那么下標就是0-10自然輸入的數據要和10來做&操作才能得到0-10之間的數字下標10的二進制位是“1010”那么意味著輸入的任何數字與10進行&操作后都得不到0001、0100、0101這些數字也就是對應到十進制的數字1、8、9。宏觀上講如果數組長度是11按照這種方式存入數據那么1、8、9這幾個數組下標下面永遠也沒數據我們預想的是想將數據離散到0-10這些位置但是有3個位置卻永遠沒數據這就是問題所在而問題的根本就是二進制位置為0的地方將永遠離散不到因此要使用這樣的方法最好選擇相應二進制位都是1的數字如1、127、255、511、1023等。

1.3.3 大量判定“是|否”的操作

如果發現一個業務系統中的許多操作都是在判定是、否很多時候大家為了節約空間不會為每一個是、否的值用一個單獨int來存儲因為那樣比較浪費空間。而一個int是4個字節所以它可以存放32bit二進制位一個二進制位就可以代表一個“是|否”。

這樣的設計是沒有問題的而且很多小伙伴們都知道這樣的設計但是胖哥發現有些可愛的同學在設計過后拿出來判定對應的位置是與否的時候是先用Integer.toBinary String(int)轉換為二進制字符串然后從這個字符串中取出對應位置的char再判定這個char是’0’還是’1’的操作。

這種操作顯然有點過了而且操作的時間復雜度很高或許我們可以學一學Java提供的一些類中是如何處理的。例如“java.lang.reflect.Modifier”它對類型的修飾符有各種各樣的判定例如字節碼中存儲public final static這樣的一長串內容只用一個數字來表達那么它是如何做到的呢請聽胖哥慢慢道來Java將這些符號進行了固定的規格編碼就像商品類型的名稱與編號一樣這個編碼會在生成class文件時存在JVM運行時也會使用。

◎ 是否為public使用十六進制數據0x00000001表示。

◎ 是否為static使用十六進制數據0x00000008表示。十六進制的8代表二進制的最后3位是100。

◎ 是否為final使用十六進制數據0x00000010表示。十六進制的10代表二進制最后4位為1000。

在Modifier類中大家可以找到private、protected、synchronized、volatile、transient、native、interface、abstract、strict等各種類型的表達數字。

Java程序在讀取字節碼時讀取到一個數字該數字只需要與對應的編碼進行“&”按位求與操作根據結果是否為0即可得到答案。設計有點小技巧吧

此時有的小伙伴就會說“很多時候需要判定它是不是public final static的這么多類型要一個個判定好麻煩。”胖哥告訴你這種方式其實還可以打組合拳。組合拳如何打呢用“或”運算具體操作就是

final?static?int?PUBLIC_FINAL_STATIC =?PUBLIC?|?FINAL?|?STATIC;

這個值將會事先保存在程序中由于這三項內容的二進制并不沖突所以做或運算就代表三者都成立的意思。當傳入參數后只需要與這個值做“按位求與”就能得到結果了。不過現在“按位求與”的結果不是與0比較而是與PUBLIC_FINAL_STATIC比較是否等值因為需要每個二進制位都要對應上才能證明它的3個特征都成立。所有的二進制位都必須要匹配上即使一個不匹配也不行示例如下

public static?boolean isPublicFinalStatic(int mod) {

return (mod &?PUBLIC_FINAL_STATIC) ==?PUBLIC_FINAL_STATIC;

}

1.3.4 簡單的數據轉換

數據轉換有兩種其中一種是進制轉換另一種是數字與byte[]的等值轉換。我們先來看看進制轉換。

◎ 將十進制數據轉換為“二進制的字符串”使用Integer.toBinaryString()、Long.toBinaryString()來操作。大家注意了這里說的是二進制的字符串數字本身就是以二進制形式存儲的在UI上要看到這個數字的二進制位所以才轉換成字符串來顯示。

◎ 將十進制數據轉換為“十六進制的字符串”使用Integer.toHexString(int)來操作。類似的在float、double、long中也有相應的方法存在。

◎ 將其他進制的數據轉換為十進制數據使用Integer.parseInt(String,int)、Integer. valueOf(String,int)來完成其中第二個參數傳入的就是字符串數據。例如Integer.valueOf(“10″,16)返回的值就是16Integer.valueOf(“10″,2)返回的值是2這樣就可以實現任意進制之間的轉換了。long也提供了類似的方法。

數字與byte[]之間的轉換是什么意思呢大家都知道在Java語言中int是由4個字節byte組成的。在網絡上發送數據的時候都是通過byte流來處理的所以會發送4個byte的內容4個byte會由高到低順序排列發送接收方反向解析。在Java中可以基于DataOutputStream、DataInputStream的writeInt(int)和readInt()來得到正確的數據代碼如圖1-6所示。

圖1-6 DataXXXStream對于數字的處理

如果使用了通道相關的技術ByteBuffer則由相關的API來實現。這是原始的實現方式如果其他語言提供的API希望將這些byte位交換位置例如要求低位先發送那么你就要自己來處理了而處理方式就可以參看DataXXXStream的處理方式。

在DataXXXStream的類中不僅僅有對int類型的處理還有對boolean、byte、unsigned byte、short、unsigned short、char、int、long、float、unsigned float、double、UTF的處理而且還有一個readFull()方法這個方法要求讀滿一個緩沖區后才返回數據它會在內部區循環處理。

在ByteBuffer的實現類中也提供了各種數據類型的put、get操作內部也是通過byte轉換來完成的。

在java.io.Bits、java.nio.Bits類中也有大量的數字與byte[]的轉換操作這兩個類我們的程序無法直接使用但是可以看源碼大家可以參考它們的代碼來實現自己的特定需求。

1.3.5 數字太大long都存放不下

long采用8個字節來存放數字它連時間的毫秒值、微秒值甚至納秒值都可以存放下它存放不下的數字很少見但是它畢竟只有8個字節如果真的遇到了某些變態的設計怎么辦呢此時需要使用BigDecimal來操作它不是以固定長度的字節來存儲數據而是以對象的方式來管理位的。我們用一個例子來看看使用BigDecimal對一個大數字操作包含幾個步驟。

首先將大數字加上10輸出結果。

然后將結果的byte位取出來以二進制形式展現。

最后根據二進制字符串反轉為數字對象。

代碼清單1-4 BigDecimal測試

01 import?java.math.BigDecimal;
02 import?java.math.BigInteger;
03?
04 public?class?BigNumberTest {
05?
06 ????private?static?String lPad(String now ,
07 ????????????????????????int?expectLength ,
08 ????????????????????????char?paddingChar) {
09 ????????if(now ==?null?|| now.length() >= expectLength) {
10 ????????????return?now;
11 ????????}
12 ????????StringBuilder buf =?new?StringBuilder(expectLength);
13 ????????for(int?i =?0?, paddingLength = expectLength - now.length();
14 ????????????????????????????i < paddingLength ; i++) {
15 ????????????buf.append(paddingChar);
16 ????????}
17 ????????return?buf.append(now).toString();
18 ????}
19?
20 ????public?static?void?main(String []args) {
21 ????????//這個數字long是放不下的
22 ????????BigDecimal bigDecimal
23 =?new?BigDecimal("1233243243243243243243243243243243241432423432");
24 ????????System.out.println("數字的原始值是"?+ bigDecimal);
25?
26 ????????//bigDecimal = bigDecimal.add(BigDecimal.TEN);
27 ????????//System.out.println("添加10以后" + bigDecimal);
28?
29 ????????//二進制數字
30 ????????byte[] bytes = bigDecimal.toBigInteger().toByteArray();
31 ????????for(byte?b : bytes) {
32 ????????????String bitString = lPad(Integer.toBinaryString(b &?0xff) ,
33 ????????????????????????????????????8?,?'0');
34 ????????????System.out.println(bitString);
35 ????????}
36 ????????//還原結果
37 ????????BigInteger bigInteger =?new?BigInteger(bytes);
38 ????????System.out.println("還原結果為"?+ bigInteger);
39 ????}
40}

在這段代碼中有一個substring()、lPad()操作。這是因為Integer.toBinaryString(b)傳入的雖然是byte但是會轉型為int如果byte的第1個bit位是1則代表是負數那么轉換為int的高24位將會填充1。其實我們不需要這24位所以用了substring()。如果是正數那么輸出的字符串會將前面的0去掉為了在顯示上使用8位二進制對齊方式所以在代碼中用了lPad()。我們再來看看輸出結果。

數字的原始值是1233243243243243243243243243243243241432423432

添加10以后1233243243243243243243243243243243241432423442

00110111

01001100

11110000

00101001

11111111

10001010

00010101

00001101

00011100

01111111

00101001

00000001

01000111

01000110

10110011

01111000

01100100

00011100

00001000

還原結果為1233243243243243243243243243243243241432423442

數字能轉換為二進制數據又能還原成數字理論上應該沒有問題大家也可以用一些比較小的數字做測試來印證這個結論例如2、15、16、1024等。不過在印證的過程中有的同學發現高位出現整個字節的8位全是0的情況例如255輸出的結果是兩個字節的二進制字符串0000000011111111。大家很疑惑既然高8位全是0為何還要單獨拿一個byte來存放呢有一點要注意了因為255是正數而一個字節中的最高位變成了1如果直接用11111111來表達就表示它是一個負數具體值是-1所以需要多一個字節來表達自己是正數。

long的存儲能力到底有多大

許多同學設計一些PK列的時候認為數字存儲不下用字符串來存放數字當然不排除某種編碼規則但是用數字往往計算快速得多而且空間也少很多。為了了解long的存儲能力有多大我們先看看int的存儲能力有多大。Integer.MAX_VALUE的值為“2147483647”也就是2G-1的大小。如何得來自然是4字節對應32位然后去掉負數和0得來的。這個值換算成十進制數據就是21億理論上很多表達不到那么大或者說絕大部分表不會有這么多ID但是若某些跳躍和歷史數據超過這個數字怎么辦那么就用long吧它的寬度是int的2倍即4G*4G/2–1為什么自己想想。十六進制數據0x7fffffffffffffffL換算為十進制數據是“9223372036854775807”這個數字看不出到底有多大我們可以和時間進行比較。

Long.MAX_VALUE / (365l * 24 * 60 * 60 * 1000) = 292471208

Long.MAX_VALUE / (365l * 24 * 60 * 60 * 1000 * 1000) = 292471

Long.MAX_VALUE / (365l * 24 * 60 * 60 * 1000 * 1000000) = 292

如果存放毫秒值則可以存放2.92億年微妙值可以存放29.2萬年納秒值可以存放292年的數據。換句話說一個表的ID即使加上跳躍每秒有10E9個ID自動增長也可以增加292年的數據不重復胖哥認為這種并發在現在的計算機時代還不存在。小伙伴們通過這種量化后自然應該理解某些設計應該如何做了吧。

數字游戲玩到這里就結束了在本書的后文中胖哥還會提到一些和數字相關的小玩意例如i++與++i的問題或許胖哥的說法與在教材中看到的解釋不一樣哦。

  • 轉載自?并發編程網 - ifeve.com

總結

以上是生活随笔為你收集整理的《Java特种兵》1.3 简单数字游戏玩一玩的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。