土曜日, 5月 11, 2013

Commons Collection 4 - CompositeMap, FastList

CompositeMap - 複数のマップを統合

複数のマップをひとつのマップインスタンスからアクセスできる,ビューのようなものを利用できれば便利だなぁ,と思ったことはないだろうか.そのようなユースケースに対してはCompositeMapを使うと良い.以下のコードはマップのユースケースだが,同じような機能がセット,ひいてはコレクションに対しても用意されている ( CompositeSet, CompositeCollection ).
注意しておきたいのは,重複したキーがあった場合にはCompositeクラスは例外をスローすることである.

 @Test(expected=Exception.class)
 public void testCompositeMap() {
  
  CompositeMap compo = new CompositeMap( initMap(), initMap2() );
  assertThat( compo.size(), is( 10 ) );
  compo = new CompositeMap( initMap(), initMap() );
  
 }
 
 private Map<Integer,String> initMap() {
  Map<Integer,String> m = new HashMap<Integer,String>();
  m.put( 1, "NewYork" );
  m.put( 2, "Tokyo" );
  m.put( 3, "Bangalore" );
  m.put( 4, "London" );
  m.put( 5, "Paris" );
  return m;
 }
 
 private Map<Integer,String> initMap2() {
  Map<Integer,String> m = new HashMap<Integer,String>();
  m.put( 6, "Hongkong" );
  m.put( 7, "Seoul" );
  m.put( 8, "Cairo" );
  m.put( 9, "AllenTown" );
  m.put( 10, "GeorgeTown" );
  return m;
 }

FastList - 非同期リードを提供するリスト

Javadocにも書いてある通り,このクラスはマルチスレッド環境下で大半がリードアクセスである場合の使用を想定している.FastListを使った場合,リストへのリードアクセスは非同期化され,ライトアクセスは以下のような処理を経る.

  1. 既存のコレクションをクローン
  2. クローンに対して変更を適用
  3. 既存のコレクションをステップ2 で変更したクローンと置き換える

FastListには slow モードと fast モードの2つがあり,slowモードはリードアクセスは同期化され,ライト時のクローンも行われない.fastモードはリードは非同期,ライトもクローンプロセスを経る.
最初にこのクラスを生成したときはslowモードなので,最初の初期化が経てあとはほとんどリードオンリー,となった段階でsetFast(true)を呼ぶ必要がある.

ちなみにFast系は以下のコードで使っているFastArrayListのほかにFastHashMapとFastTreeMapがある.

コード
このコードはFastArrayListとArrayListの同じノードに対してそれぞれ100回アクセスした場合のラップタイムを計っている. 結果は何度やってもArrayListのほうが約2倍近く早い.おそらくFastArrayListは実装が色々と複雑となってしまっているのだろう.

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.commons.collections.FastArrayList;
import org.apache.commons.lang3.time.StopWatch;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

public class FastListTest implements Runnable {

 List<String> list = null;

 @Before
 public void init()  {
  
  list = initFastList();

 }
 
 @Test
 public void testFastList() throws InterruptedException {
  
  ExecutorService service = Executors.newFixedThreadPool( 30 );
  StopWatch watch = new StopWatch();
  watch.start();
  for( int i = 0;i < 100;i++ ) {
   service.submit( this );
   
  }
  service.shutdown();
  service.awaitTermination( 10000L, TimeUnit.HOURS );
  System.out.println( "FAST - " + watch.getTime() );
  
  watch.reset();
  
  list = initArrayList();
  service = Executors.newFixedThreadPool( 30 );
  
  watch.start();
  for( int i = 0;i < 100;i++ ) {
   service.submit( this );
   
  }
  service.shutdown();
  service.awaitTermination( 10000L, TimeUnit.HOURS );
  System.out.println( "NONFAST - " + watch.getTime() );
  
 }

 private FastArrayList initFastList() {
  
  // slow mode first
  FastArrayList list = new FastArrayList();
  list.add( "a" );
  // switching to fast mode
  list.setFast( true );
  return list;
  
 }
 
 private List<String> initArrayList() {
  
  // slow mode first
  List<String> list = new ArrayList<String>();
  list.add( "a" );
  // switching to fast mode
  return list;
  
 }

 public void run() {
  // access to random node
  list.get( 0 );
  
 }
}

0 件のコメント: