使用線程安全的navigablemap
java api 提供的有趣的資料結構,并且你可以在并發應用程式中使用,它就是concurrentnavigablemap接口的定義。實作concurrentnavigablemap接口的類存儲以下兩部分元素:
唯一辨別元素的key
定義元素的剩餘資料
每部分在不同的類中實作。
java api 也提供了這個接口的實作類,這個類是concurrentskiplistmap,它實作了非阻塞清單且擁有concurrentnavigablemap的行為。在内部實作中,它使用skip list來存儲資料。skip list是基于并行清單的資料結構,它允許我們擷取類似二叉樹的效率。使用它,你可以得到一個排序的資料結構,這比排序數列使用更短的通路時間來插入、搜尋和删除元素。
注意:在1990年,由william pugh引入skip list。
當你往map中插入資料時,它使用key來排序它們,是以,所有元素将是有序的。除了傳回具體的元素,這個類也提供了擷取map的子map的方法。
在這個指南中,你将學習如何使用concurrentskiplistmap類來實作一個通訊錄的map。
準備工作…
這個指南的例子使用eclipse ide實作。如果你使用eclipse或其他ide,如netbeans,打開它并建立一個新的java項目。
如何做…
按以下步驟來實作的這個例子:
1.建立一個contact類。
<code>1</code>
<code>public</code> <code>class</code> <code>contact {</code>
2.聲明兩個私有的、string類型的屬性name和phone。
<code>private</code> <code>string name;</code>
<code>2</code>
<code>private</code> <code>string phone;</code>
3.實作這個類的構造器,并初始化它的屬性。
<code>public</code> <code>contact(string name, string phone) {</code>
<code>this</code><code>.name=name;</code>
<code>3</code>
<code>this</code><code>.phone=phone;</code>
<code>4</code>
<code>}</code>
4.實作傳回name和phone屬性值的方法。
<code>public</code> <code>string getname() {</code>
<code>return</code> <code>name;</code>
<code>public</code> <code>string getphone() {</code>
<code>5</code>
<code>return</code> <code>phone;</code>
<code>6</code>
5.建立一個task類,并指定它實作runnable接口。
<code>public</code> <code>class</code> <code>task</code><code>implements</code> <code>runnable {</code>
6.聲明一個私有的、參數化為string類和contact類的concurrentskiplistmap類型的屬性map。
<code>private</code> <code>concurrentskiplistmap<string, contact> map;</code>
7.聲明一個私有的、string類型的屬性id,用來存儲目前任務的id。
<code>01</code>
<code>private</code> <code>string id;</code>
<code>02</code>
<code>03</code>
<code>[/code[</code>
<code>04</code>
<code>05</code>
<code>8</code><code>.實作這個類的構造器,用來存儲它的屬性。</code>
<code>06</code>
<code>07</code>
<code>08</code>
<code>09</code>
<code>public</code> <code>task (concurrentskiplistmap<string, contact> map, string</code>
<code>10</code>
<code>id) {</code>
<code>11</code>
<code>this</code><code>.id=id;</code>
<code>12</code>
<code>this</code><code>.map=map;</code>
<code>13</code>
9.實作run()方法。使用任務的id和建立contact對象的增長數,在map中存儲1000個不同的通訊錄。使用put()方法添加通訊錄到map中。
<code>@override</code>
<code>public</code> <code>void</code> <code>run() {</code>
<code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i<</code><code>1000</code><code>; i++) {</code>
<code>contact contact=</code><code>new</code> <code>contact(id, string.valueof(i+</code><code>1000</code><code>));</code>
<code>map.put(id+contact.getphone(), contact);</code>
<code>7</code>
10.通過建立main類,并添加main()方法來實作這個例子的主類。
<code>public</code> <code>class</code> <code>main {</code>
<code>public</code> <code>static</code> <code>void</code> <code>main(string[] args) {</code>
11.建立一個參數化為string類和contact類的concurrentskiplistmap對象map。
<code>concurrentskiplistmap<string, contact> map;</code>
<code>map=</code><code>new</code> <code>concurrentskiplistmap<>();</code>
12.建立一個有25個thread對象的數組,用來存儲你将要執行的所有任務。
<code>thread threads[]=</code><code>new</code> <code>thread[</code><code>25</code><code>];</code>
<code>int</code> <code>counter=</code><code>0</code><code>;</code>
13.建立和啟動25個任務,對于每個任務指定一個大寫字母作為id。
<code>for</code> <code>(</code><code>char</code> <code>i=</code><code>'a'</code><code>; i<</code><code>'z'</code><code>; i++) {</code>
<code>task task=</code><code>new</code> <code>task(map, string.valueof(i));</code>
<code>threads[counter]=</code><code>new</code> <code>thread(task);</code>
<code>threads[counter].start();</code>
<code>counter++;</code>
14.使用join()方法等待線程的結束。
<code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i<</code><code>25</code><code>; i++) {</code>
<code>try</code> <code>{</code>
<code>threads[i].join();</code>
<code>}</code><code>catch</code> <code>(interruptedexception e) {</code>
<code>e.printstacktrace();</code>
15.使用firstentry()方法擷取map的第一個實體,并将它的資料寫入到控制台。
<code>system.out.printf(</code><code>"main: size of the map: %d\n"</code><code>,map.size());</code>
<code>map.entry<string, contact> element;</code>
<code>contact contact;</code>
<code>element=map.firstentry();</code>
<code>contact=element.getvalue();</code>
<code>system.out.printf(</code><code>"main: first entry: %s: %s\n"</code><code>,contact.</code>
<code>getname(),contact.getphone());</code>
16.使用lastentry()方法擷取map的最後一個實體,并将它的資料寫入到控制台。
<code>element=map.lastentry();</code>
<code>system.out.printf(</code><code>"main: last entry: %s: %s\n"</code><code>,contact.</code>
17.使用submap()方法擷取map的子map,并将它們的資料寫入到控制台。
<code>system.out.printf(</code><code>"main: submap from a1996 to b1002: \n"</code><code>);</code>
<code>concurrentnavigablemap<string, contact> submap=map.</code>
<code>submap(</code><code>"a1996"</code><code>,</code><code>"b1002"</code><code>);</code>
<code>do</code> <code>{</code>
<code>element=submap.pollfirstentry();</code>
<code>if</code> <code>(element!=</code><code>null</code><code>) {</code>
<code>system.out.printf(</code><code>"%s: %s\n"</code><code>,contact.getname(),contact.</code>
<code>getphone());</code>
<code>}</code><code>while</code> <code>(element!=</code><code>null</code><code>);</code>
它是如何工作的...
在這個指南中,我們已實作task類來存儲contact對象到navigablemap 中。每個通訊錄都有一個名稱(建立它的任務的id的)和電話号碼(1000到2000之間的數字)。我們已使用這些值的連續值作為通訊錄的key。每個task對象建立1000個通訊錄,并使用put()方法将它們存儲到navigablemap中。
注意:如果你插入的key已存在,那麼這個key的元素将被新的元素取代。
main類的main()方法建立25個task對象,并使用a-z的字母作為ids。然後,你已使用一些方法從map中擷取資料。firstentry()方法傳回map第一個元素的map.entry對象,且不會删除這個元素。這個對象包含key和元素。你已調用getvalue()方法來擷取元素。你可以使用getkey()來擷取元素的key。
lastentry()方法傳回map最後一個元素的map.entry對象,submap()方法傳回map的部分元素的concurrentnavigablemap對象。在這個例子中,元素擁有a1996到b1002之間的key。在這種情況下,你可以使用pollfirst()方法來處理submap()方法傳回的這些元素。這個方法将傳回并删除submap中的第一個map.entry對象。
以下截圖顯示了程式執行的輸出:

不止這些...
concurrentskiplistmap類有其他有趣的方法,這些方法如下:
headmap(k tokey):k是參數化concurrentskiplistmap對象的key值的類。傳回此映射的部分視圖,其鍵值小于 tokey。
tailmap(k fromkey):k是參數化concurrentskiplistmap對象的key值的類。傳回此映射的部分視圖,其鍵大于等于 fromkey。
putifabsent(k key, v value):如果key不存在map中,則這個方法插入指定的key和value。
polllastentry():這個方法傳回并删除map中最後一個元素的map.entry對象。
replace(k key, v value):如果這個key存在map中,則這個方法将指定key的value替換成新的value。
參見
在第6章,并發集合中的使用非阻塞線程安全的數列指南