Javaを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計 (リスト&マップ編)

Javaでのリスト&マップを使用したアルゴリズムの書き方の覚書です。

説明に使用するデータ構造

アルゴリズムの説明のために、以下のようなマップのリストを使います。

List> countries = new ArrayList>();

Map country;

country = new HashMap();
country.put( "name",       "日本" );
country.put( "currency",   "JPY" );
country.put( "population", 127156000 );
countries.add( country );

country = new HashMap();
country.put( "name", "フランス" );
country.put( "currency", "EUR" );
country.put( "population", 65073482 );
countries.add( country );

country = new HashMap();
country.put( "name", "スペイン" );
country.put( "currency", "EUR" );
country.put( "population", 44904000 );
countries.add( country );

country = new HashMap();
country.put( "name", "ロシア" );
country.put( "currency", "RUB" );
country.put( "population", 141903979 );
countries.add( country );

country = new HashMap();
country.put( "name", "ベトナム" );
country.put( "currency", "VND" );
country.put( "population", 84238000 );
countries.add( country );

country = new HashMap();
country.put( "name", "カンボジア" );
country.put( "currency", "KHR" );
country.put( "population", 14805000 );
countries.add( country );

country = new HashMap();
country.put( "name", "コートジボワール" );
country.put( "currency", "XOF" );
country.put( "population", 44904000 );
countries.add( country );

このマップのリストの内容は、以下のようなコードで表示できます。

//////////////////////////////////////////////////////////////////
// 各項目の最大長を求める。
//////////////////////////////////////////////////////////////////
int nameLen = length( "名前" );
int currencyLen = length( "通貨" );
int populationLen = length( "人口" );
for ( int i = 0; i < countries.size(); ++i ) {
	Map country = countries.get( i );
	String name =  country.get( "name" ).toString();
	String currency =  country.get( "currency" ).toString();
	String population =  country.get( "population" ).toString();
	
	if ( length( name ) > nameLen ) {
		nameLen = length( name );
	}
	if ( length( currency ) > currencyLen ) {
		currencyLen = length( currency );
	}
	if ( length( population )  > populationLen ) {
		populationLen = length( population );
	}
}

//////////////////////////////////////////////////////////////////
// データをテーブルの形で出力する。
//////////////////////////////////////////////////////////////////

// 区切り文字列を作成する。
String separator = "+" + padding( "", nameLen, '-' )
	+ "+" + padding( "", currencyLen, '-' )
	+ "+" + padding( "", populationLen, '-' ) + "+";

// 区切り文字列を出力する。
System.out.println( separator );

// ヘッダ文字列を出力する。
System.out.println( "|" + padding( "名前", nameLen )
	+ "|" + padding( "通貨", currencyLen )
	+ "|" + padding( "人口", populationLen ) + "|" );

// 区切り文字列を出力する。
System.out.println( separator );

// データを出力する。
for ( int i = 0; i < countries.size(); ++i ) {
	Map country = countries.get( i );
	String name =  country.get( "name" ).toString();
	String currency =  country.get( "currency" ).toString();
	String population =  country.get( "population" ).toString();
	
	System.out.println( "|" + padding( name, nameLen )
		+ "|" + padding( currency, currencyLen )
		+ "|" + padding( population, populationLen ) + "|" );
}

// 区切り文字列を出力する。
System.out.println( separator );

…

//////////////////////////////////////////////////////////////////
// 以下、ユーティリティメソッド
//////////////////////////////////////////////////////////////////

/* 文字列のバイト数を取得する。 */
private static int length( String str ) {
	return str.getBytes().length;
}

/* 文字列を空白でパディングする。 */
private static String padding( String str, int length ) {
	return padding( str, length, ' ' );
}

/* 文字列を指定した文字でパディングする。 */
private static String padding( String str, int length, char padChar ) {
	int spaceCount = length - str.getBytes().length;
	
	StringBuffer buf = new StringBuffer();
	for ( int i = 0; i < spaceCount; ++i ) {
		buf.append( padChar );
	}
	
	return str + buf.toString();
}

このコードの実行結果は以下の通りです。

+----------------+----+---------+
|名前            |通貨|人口     |
+----------------+----+---------+
|日本            |JPY |127156000|
|フランス        |EUR |65073482 |
|スペイン        |EUR |44904000 |
|ロシア          |RUB |141903979|
|ベトナム        |VND |84238000 |
|カンボジア      |KHR |14805000 |
|コートジボワール|XOF |44904000 |
+----------------+----+---------+

■抽出

複数のデータの中から、特定の条件を満たすデータを抽出するアルゴリズムです。
例として、商品データproductsから特定の条件を満たすデータを抽出します。

List> products = new ArrayList>();

Map product;

product = new HashMap();
product.put( "product_id", "1010" );
product.put( "product_name", "えんぴつ" );
product.put( "price", 80 );
products.add( product );

product = new HashMap();
product.put( "product_id", "1020" );
product.put( "product_name", "ボールペン" );
product.put( "price", 100 );
products.add( product );

product = new HashMap();
product.put( "product_id", "1030" );
product.put( "product_name", "消しゴム" );
product.put( "price", 100 );
products.add( product );

product = new HashMap();
product.put( "product_id", "2010" );
product.put( "product_name", "定規" );
product.put( "price", 140 );
products.add( product );

product = new HashMap();
product.put( "product_id", "2020" );
product.put( "product_name", "コンパス" );
product.put( "price", 300 );
products.add( product );

product = new HashMap();
product.put( "product_id", "3010" );
product.put( "product_name", "のり" );
product.put( "price", 200 );
products.add( product );

商品データproductsを表示すると、以下のようになります。

+------+----------+----+
|商品ID|商品名    |価格|
+------+----------+----+
|1010  |えんぴつ  |80  |
|1020  |ボールペン|100 |
|1030  |消しゴム  |100 |
|2010  |定規      |140 |
|2020  |コンパス  |300 |
|3010  |のり      |200 |
+------+----------+----+


以下のコードは、商品データproductsから「price > 100」を満たすデータを抽出します。

// 抽出結果を格納するリスト
List> results = new ArrayList>();

// すべての商品データについて繰り返す。
for ( int i = 0; i < products.size(); ++i ) {
	product = products.get( i );
	
	// 商品データのpriceが100より大きいことを確認する。
	int price = (Integer) product.get( "price" );
	if ( price > 100 ) {
		// 商品データを抽出結果に追加する。
		results.add( product );
	}
}

抽出結果は、変数resultsに格納されます。抽出結果resultを表示すると、以下のようになります。

+------+--------+----+
|商品ID|商品名  |価格|
+------+--------+----+
|2010  |定規    |140 |
|2020  |コンパス|300 |
|3010  |のり    |200 |
+------+--------+----+

■ソート

複数のデータを、特定の条件でソートするアルゴリズムです

Javaでのリストのソート

ソートの例の前に、Javaでのリストのソートについて説明します。
リストをソートするには、Collectionsクラスのsort()メソッドを使用します。

List list = new ArrayList();
list.add( 13 );
list.add( 43 );
list.add( 22 );
list.add( 234 );

Collections.sort( list, new Comparator() {
	public  int compare( Integer a, Integer b ) {
		// 引数1と引数2はそれぞれリスト中の値
		
		// compareTo()メソッドにより引数の大小比較を行う。
		// ・引数2の方が大きければ、戻り値は正の整数: 引数2→引数1の順に並ぶ。
		// ・引数同士が一致すれば、戻り値は0: 引数1と引数2は同列。
		// ・引数1の方が大きければ、戻り値は負の整数: 引数1→引数2の順に並ぶ。
		return  b.compareTo( a );
	}
} );

ソートの並び順は、sort()メソッドの引数に渡すComparatorの実装クラス中のcompare()メソッドで決めます。

compare()メソッドには引数としてリストの要素が2つずつ渡されます。引数2→引数1の順に並べたい場合は正の値を、引数1と引数2を同列にしたい場合は0を、引数1→引数2の順に並べたい場合は負の値を、compare()メソッドの戻り値として返します。

なお、上記のコードはリストの要素を「数値の降順」にソートします。

ソートの例(五十音順)

それでは、ソートの例を挙げます。
例として、売上データsalesを特定の項目の「五十音順」にソートします。

List> sales = new ArrayList>();
Map sale;

sale = new HashMap();
sale.put( "sales_date", "20090702" );
sale.put( "sales_no", "00001" );
sale.put( "product_id", "1020" );
sale.put( "amount", 10 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090702" );
sale.put( "sales_no", "00002" );
sale.put( "product_id", "1010" );
sale.put( "amount", 5 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090702" );
sale.put( "sales_no", "00003" );
sale.put( "product_id", "3010" );
sale.put( "amount", 20 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090703" );
sale.put( "sales_no", "00001" );
sale.put( "product_id", "1030" );
sale.put( "amount", 10 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090703" );
sale.put( "sales_no", "00002" );
sale.put( "product_id", "1020" );
sale.put( "amount", 15 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090704" );
sale.put( "sales_no", "00001" );
sale.put( "product_id", "2020" );
sale.put( "amount", 30 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090704" );
sale.put( "sales_no", "00002" );
sale.put( "product_id", "3010" );
sale.put( "amount", 20 );
sales.add( sale );

売上データsalesを表示すると、以下のようになります。

+--------+--------+------+----+
|売上日  |売上番号|商品ID|数量|
+--------+--------+------+----+
|20090702|00001   |1020  |10  |
|20090702|00002   |1010  |5   |
|20090702|00003   |3010  |20  |
|20090703|00001   |1030  |10  |
|20090703|00002   |1020  |15  |
|20090704|00001   |2020  |30  |
|20090704|00002   |3010  |20  |
+--------+--------+------+----+


以下のコードは、売上データsalesを「五十音順でsales_noの昇順」にソートします。

Collections.sort( sales, new Comparator>() {
	public int compare( Map sale1, Map sale2 ) {
		// 引数1と引数2はそれぞれ売上データ。
		
		// 売上データからsales_noを取得する。
		String sales_no1 = sale1.get( "sales_no" ).toString();
		String sales_no2 = sale2.get( "sales_no" ).toString();

		////////////////////////////////////////////
		// sales_noを比較する。
		////////////////////////////////////////////

		// compareTo()メソッドによりproduct_idの大小を比較する。
		// ・引数2のproduct_idの方が大きければ、戻り値は正の整数: 引数2→引数1の順に並ぶ。
		// ・product_idが一致すれば、戻り値は0: 引数1と引数2の同列。
		// ・引数1のproduct_idの方が大きければ、戻り値は負の整数: 引数1→引数2の順に並ぶ。
		return sales_no1.compareTo( sales_no2 );
	}
} );

ソート後の売上データsalesを表示すると、以下のようになります。

+--------+--------+------+----+
|売上日  |売上番号|商品ID|数量|
+--------+--------+------+----+
|20090702|00001   |1020  |10  |
|20090703|00001   |1030  |10  |
|20090704|00001   |2020  |30  |
|20090702|00002   |1010  |5   |
|20090703|00002   |1020  |15  |
|20090704|00002   |3010  |20  |
|20090702|00003   |3010  |20  |
+--------+--------+------+----+
ソートの例(数値順)

例をもう一つ挙げます。商品データproductsを特定の項目の「数値順」にソートします。

List> products = new ArrayList>();
Map product;

product = new HashMap();
product.put( "product_id", "2010" );
product.put( "product_name", "定規" );
product.put( "price", 140 );
products.add( product );

product = new HashMap();
product.put( "product_id", "2020" );
product.put( "product_name", "コンパス" );
product.put( "price", 300 );
products.add( product );

product = new HashMap();
product.put( "product_id", "3010" );
product.put( "product_name", "のり" );
product.put( "price", 200 );
products.add( product );

product = new HashMap();
product.put( "product_id", "1030" );
product.put( "product_name", "消しゴム" );
product.put( "price", 100 );
products.add( product );

product = new HashMap();
product.put( "product_id", "1020" );
product.put( "product_name", "ボールペン" );
product.put( "price", 100 );
products.add( product );

product = new HashMap();
product.put( "product_id", "1010" );
product.put( "product_name", "えんぴつ" );
product.put( "price", 80 );
products.add( product );

商品データproductsを表示すると、以下のようになります。

+------+----------+----+
|商品ID|商品名    |価格|
+------+----------+----+
|2010  |定規      |140 |
|2020  |コンパス  |300 |
|3010  |のり      |200 |
|1030  |消しゴム  |100 |
|1020  |ボールペン|100 |
|1010  |えんぴつ  |80  |
+------+----------+----+


以下のコードは、商品データproductsを「数値順でproduct_idの降順」にソートします。

Collections.sort( products, new Comparator>() {
	public int compare( Map product1, Map product2 ) {
		// 引数1と引数2はそれぞれ商品データ。
		
		// 商品データのproduct_idを取得する。
		String product_id1 = product1.get( "product_id" ).toString();
		String product_id2 = product2.get( "product_id" ).toString();
	
		////////////////////////////////////////////
		// product_idを比較する。
		////////////////////////////////////////////

		// compareTo()メソッドによりproduct_idの大小を比較する。
		// ・引数2のproduct_idの方が大きければ、戻り値は正の整数: 引数2→引数1の順に並ぶ。
		// ・product_idが一致すれば、戻り値は0: 引数1と引数2の同列。
		// ・引数1のproduct_idの方が大きければ、戻り値は負の整数: 引数1→引数2の順に並ぶ。
		return product_id2.compareTo( product_id1 );
	}
 } );

ソート後の商品データproductsを表示すると、以下のようになります。

+------+----------+----+
|商品ID|商品名    |価格|
+------+----------+----+
|3010  |のり      |200 |
|2020  |コンパス  |300 |
|2010  |定規      |140 |
|1030  |消しゴム  |100 |
|1020  |ボールペン|100 |
|1010  |えんぴつ  |80  |
+------+----------+----+

■結合

2種類のデータを、特定の条件で結合するアルゴリズムです。
例として、商品データproductsと売上データsalesを結合します。

List> products = new ArrayList>();

Map product;

product = new HashMap();
product.put( "product_id", "1010" );
product.put( "product_name", "えんぴつ" );
product.put( "price", 80 );
products.add( product );

product = new HashMap();
product.put( "product_id", "1020" );
product.put( "product_name", "ボールペン" );
product.put( "price", 100 );
products.add( product );

product = new HashMap();
product.put( "product_id", "1030" );
product.put( "product_name", "消しゴム" );
product.put( "price", 100 );
products.add( product );

product = new HashMap();
product.put( "product_id", "2010" );
product.put( "product_name", "定規" );
product.put( "price", 140 );
products.add( product );

product = new HashMap();
product.put( "product_id", "2020" );
product.put( "product_name", "コンパス" );
product.put( "price", 300 );
products.add( product );

product = new HashMap();
product.put( "product_id", "3010" );
product.put( "product_name", "のり" );
product.put( "price", 200 );
products.add( product );


List> sales = new ArrayList>();

Map sale;

sale = new HashMap();
sale.put( "sales_date", "20090702" );
sale.put( "sales_no", "00001" );
sale.put( "product_id", "1020" );
sale.put( "amount", 10 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090702" );
sale.put( "sales_no", "00002" );
sale.put( "product_id", "1010" );
sale.put( "amount", 5 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090702" );
sale.put( "sales_no", "00003" );
sale.put( "product_id", "3010" );
sale.put( "amount", 20 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090703" );
sale.put( "sales_no", "00001" );
sale.put( "product_id", "1030" );
sale.put( "amount", 10 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090703" );
sale.put( "sales_no", "00002" );
sale.put( "product_id", "1020" );
sale.put( "amount", 15 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090704" );
sale.put( "sales_no", "00001" );
sale.put( "product_id", "2020" );
sale.put( "amount", 30 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090704" );
sale.put( "sales_no", "00002" );
sale.put( "product_id", "3010" );
sale.put( "amount", 20 );
sales.add( sale );

商品データproductsを表示すると以下のようになります。

+------+----------+----+
|商品ID|商品名    |価格|
+------+----------+----+
|1010  |えんぴつ  |80  |
|1020  |ボールペン|100 |
|1030  |消しゴム  |100 |
|2010  |定規      |140 |
|2020  |コンパス  |300 |
|3010  |のり      |200 |
+------+----------+----+

また、売上データsalesを表示すると以下のようになります。

+--------+--------+------+----+
|売上日  |売上番号|商品ID|数量|
+--------+--------+------+----+
|20090702|00001   |1020  |10  |
|20090702|00002   |1010  |5   |
|20090702|00003   |3010  |20  |
|20090703|00001   |1030  |10  |
|20090703|00002   |1020  |15  |
|20090704|00001   |2020  |30  |
|20090704|00002   |3010  |20  |
+--------+--------+------+----+


以下のコードは、商品データと売上データをproduct_idをキーとして結合します。

/////////////////////////////////////////////////////////////////////////////////
// ハッシュテーブルを作成し、 product_idをキーとして商品データを取得できるようにする。
/////////////////////////////////////////////////////////////////////////////////

// ハッシュテーブル
Map> hashTable = new HashMap>();

// すべての商品データについて繰り返す。
for ( int i = 0; i < products.size(); ++i ) {
	product = products.get( i );
	
	// 商品データのproduct_idを取得する。
	Object key = product.get( "product_id" );
	
	// product_idをキーとして、商品データをハッシュテーブルに格納する。
	hashTable.put( key, product );
}

/////////////////////////////////////////////////////////////////////////////////
// 売上データと商品データを結合する。
/////////////////////////////////////////////////////////////////////////////////

// すべての売上データについて繰り返す。
for ( int i = 0; i < sales.size(); ++i ) {
	sale = sales.get( i );
	
	// 売上データのproduct_idを取得する。
	Object product_id = sale.get( "product_id" );
	
	// product_idをキーとして、商品データをハッシュテーブルから取得する。
	product = hashTable.get( product_id );
	if ( product != null ) {
		// 商品データを取得できた場合:
		
		// 商品データの値を売上データに格納する。
		sale.put( "product_name", product.get( "product_name" ) );
		sale.put( "price", product.get( "price" ) );
	}
}

結合後の売上データsalsesを表示すると、以下のようになります。

+--------+--------+------+----------+----+----+
|売上日  |売上番号|商品ID|商品名    |数量|価格|
+--------+--------+------+----------+----+----+
|20090702|00001   |1020  |ボールペン|10  |100 |
|20090702|00002   |1010  |えんぴつ  |5   |80  |
|20090702|00003   |3010  |のり      |20  |200 |
|20090703|00001   |1030  |消しゴム  |10  |100 |
|20090703|00002   |1020  |ボールペン|15  |100 |
|20090704|00001   |2020  |コンパス  |30  |300 |
|20090704|00002   |3010  |のり      |20  |200 |
+--------+--------+------+----------+----+----+

■集計

複数のデータを特定の条件で集計するアルゴリズムです。
例として、売上データsalesを集計します。

List> sales = new ArrayList>();

Map sale;

sale = new HashMap();
sale.put( "sales_date", "20090702" );
sale.put( "sales_no", "00001" );
sale.put( "product_name", "ボールペン" );
sale.put( "price", 100 );
sale.put( "amount", 10 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090702" );
sale.put( "sales_no", "00002" );
sale.put( "product_name", "えんぴつ" );
sale.put( "price", 80 );
sale.put( "amount", 5 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090702" );
sale.put( "sales_no", "00003" );
sale.put( "product_name", "のり" );
sale.put( "price", 200 );
sale.put( "amount", 20 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090703" );
sale.put( "sales_no", "00001" );
sale.put( "product_name", "消しゴム" );
sale.put( "price", 100 );
sale.put( "amount", 10 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090703" );
sale.put( "sales_no", "00002" );
sale.put( "product_name", "ボールペン" );
sale.put( "price", 100 );
sale.put( "amount", 15 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090704" );
sale.put( "sales_no", "00001" );
sale.put( "product_name", "コンパス" );
sale.put( "price", 300 );
sale.put( "amount", 30 );
sales.add( sale );

sale = new HashMap();
sale.put( "sales_date", "20090704" );
sale.put( "sales_no", "00002" );
sale.put( "product_name", "のり" );
sale.put( "price", 200 );
sale.put( "amount", 20 );
sales.add( sale );

売上データsalesを表示すると以下のようになります。

+--------+--------+----------+----+----+
|売上日  |売上番号|商品名    |価格|数量|
+--------+--------+----------+----+----+
|20090702|00001   |ボールペン|100 |10  |
|20090702|00002   |えんぴつ  |80  |5   |
|20090702|00003   |のり      |200 |20  |
|20090703|00001   |消しゴム  |100 |10  |
|20090703|00002   |ボールペン|100 |15  |
|20090704|00001   |コンパス  |300 |30  |
|20090704|00002   |のり      |200 |20  |
+--------+--------+----------+----+----+


以下のコードは、salesを集計してsales_date毎の合計数量と合計売上高を求めます。

///////////////////////////////////////////////////////
// 集計結果の初期化
///////////////////////////////////////////////////////

// 集計結果を一時的に格納するマップ
Map> resultsMap = new HashMap>();

// すべての売上データについて繰り返す。
for ( int i = 0; i < sales.size(); ++i ) {
	sale = sales.get( i );

	// 売上データのsales_dateを取得する。
	Object sales_date = sale.get( "sales_date" );
	
	if ( ! resultsMap.containsKey( sales_date ) ) {
		// マップにキーsales_dateが含まれていない:
		
		// 集計結果の初期値を生成する。
		Map result = new HashMap();
		result.put( "sales_date", sales_date );
		result.put( "amount", 0 );
		result.put( "total", 0 );

		// 生成した初期値をキーsales_dateに関連付けてマップに格納する。
		resultsMap.put( sales_date, result );
	}
}	

///////////////////////////////////////////////////////
// salesを集計
///////////////////////////////////////////////////////

// すべての売上データについて繰り返す。
for ( int i = 0; i < sales.size(); ++i ) {
	sale = sales.get( i );
	
	// 売上データのsales_dateを取得する。
	Object sales_date = sale.get( "sales_date" );
	
	// キーsales_dateに関連付く集計結果をマップから取得する。
	Map result = resultsMap.get( sales_date );

	// 売上データのamountを集計結果に足し合わせる。
	int s_amount = (Integer) sale.get( "amount" );
	int r_amount = (Integer) result.get( "amount" );
	result.put( "amount", r_amount + s_amount );
	// 売上データのpriceとamountとを掛けた値を集計結果に足し合わせる。
	int s_price = (Integer) sale.get( "price" );
	int r_total = (Integer) result.get( "total" );
	result.put( "total", r_total + s_price * s_amount );
}

///////////////////////////////////////////////////////
// 集計結果をリストに移し替え
///////////////////////////////////////////////////////

List> results = new ArrayList>();

// 一時マップの全てのキーとデータについて繰り返す。
for ( Object sales_date : resultsMap.keySet() ) {
	Map result = resultsMap.get( sales_date );
	
	// データをリストに追加する
	results.add( result );
}

///////////////////////////////////////////////////////
// 集計結果をsales_dateの昇順にソート
///////////////////////////////////////////////////////

Collections.sort( results, new Comparator>() {
	public int compare( Map result1, Map result2 ) {
		String sales_date1 = result1.get( "sales_date" ).toString();
		String sales_date2 = result2.get( "sales_date" ).toString();

		return sales_date1.compareTo( sales_date2 );
	}
} );

集計結果は変数resultsに格納されます。集計結果resultsを表示すると以下のようになります。

+--------+--------+----------+
|売上日  |合計数量|合計売上高|
+--------+--------+----------+
|20090702|35      |5400      |
|20090703|25      |2500      |
|20090704|50      |13000     |
+--------+--------+----------+