天天看點

并發集合(六)使用線程安全的NavigableMap

使用線程安全的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&lt;string, contact&gt; 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&lt;string, contact&gt; 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&lt;</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&lt;string, contact&gt; map;</code>

<code>map=</code><code>new</code> <code>concurrentskiplistmap&lt;&gt;();</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&lt;</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&lt;</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&lt;string, contact&gt; 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&lt;string, contact&gt; 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對象。

以下截圖顯示了程式執行的輸出:

并發集合(六)使用線程安全的NavigableMap

不止這些...

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章,并發集合中的使用非阻塞線程安全的數列指南

繼續閱讀