Javaを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計 (リスト&マップ編)
Javaでのリスト&マップを使用したアルゴリズムの書き方の覚書です。
説明に使用するデータ構造
アルゴリズムの説明のために、以下のようなマップのリストを使います。
List
このマップのリストの内容は、以下のようなコードで表示できます。
////////////////////////////////////////////////////////////////// // 各項目の最大長を求める。 ////////////////////////////////////////////////////////////////// int nameLen = length( "名前" ); int currencyLen = length( "通貨" ); int populationLen = length( "人口" ); for ( int i = 0; i < countries.size(); ++i ) { Mapcountry = 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()メソッドを使用します。
Listlist = 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
結合後の売上データ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 | +--------+--------+----------+
データ構造
アルゴリズム
- Javaを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計 (リスト&マップ編)
- Javaを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計 (リスト&ビーン編)
- PHPを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計
- VBAを使うなら理解しておきたいアルゴリズム - 抽出・結合・集計
- Javascriptを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計
- SQLを使うなら理解しておきたいアルゴリズム?(というか、select文の書き方) - where・order by・join・group by
- Bashを使うなら理解しておきたいアルゴリズム - 抽出・ソート・結合・集計