本文へジャンプ

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

送信されたすべての情報は安全です。

  • 閉じる [x]

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


送信されたすべての情報は安全です。

  • 閉じる [x]

Merlinの魔術: 挿入順序を保持する

新たにリンクされたHashSetとHashMapの実装を使ってみる

John Zukowski (jaz@zukowski.net), President, JZ Ventures, Inc.
Author photo
John Zukowskiは、JZ Ventures, Inc.の戦略的Javaコンサルティングを推進し、数多くのjGuruのコミュニティー主導のJava FAQsで常任指導者として活躍しています。最新の著書には、Apressから出版された「 Java Collections」および「 Definitive Guide to Swing for Java 2」 (第2版) があります。彼のメール・アドレスはjaz@zukowski.net です。

概要: J2SEバージョン1.4の新たなCollections Framework実装で取り入れられた、ハッシュ・コレクションの要素を挿入順にたどる方法と、アクセス順に要素を保持する方法についてJohn Zukowskiが解説します。それでは流れに沿って見ていきましょう。

日付:  2001年 8月 01日
レベル:  初級 この記事の原文:  英語
アクティビティー: 2777 ビュー
お気軽にご意見・ご感想をお寄せください: 


J2SE 1.4では、Java Collections FrameworkにLinkedHashSetLinkedHashMap の2つの新たな実装を取り入れています。これらを追加する利点は、ハッシュ・コレクションがその要素に対して2種類のたどり方を保持できるということです。標準のハッシュ関係に加えて、コレクション全体を横断するリンクによるリストが備わります。通常では、この新しい2番目のたどり方は挿入順序に従います。つまり、コレクションの反復子が要素を(ハッシュ・コードがこれらをコレクションに組み込んだ順序ではなく)その挿入順に返すことになります。しかし、LinkedHashMapは2つめの順序付けオプションをサポートします。つまり挿入順ではなく、アクセス順にリンクによるリストを保持します。

では、これらの新しいクラスがどのように機能するか見てみましょう。

はじめに

これらの新しいクラスを試すのは、簡単です。java.util パッケージをインポートして、ここで使う項目の集合を見つけるだけです。この例では暦月を使用することにします。セットで作業する場合は英語の月名を使用し、マップで作業する場合は英語とイタリア語の月名を使用します。


リスト1. クラス定義の開始
                
import java.util.*;
public class OrderedAccess {
  public static void main(String args[]) {
    String months[] = new DateFormatSymbols().getMonths();
    String italianMonths[] = new DateFormatSymbols(Locale.ITALIAN).getMonths();
  }
}

ここでは、皆さんがすでに英語の月名と順序をご存じのものと想定します。イタリア語の月名になじみのない皆さんのためにここで紹介しておきますが、Gennaio、Febbraio、Marzo、Aprile、Maggio、Giugno、Luglio、Agosto、Settembre、Ottobre、Novembre、Dicembreとなります。ただし、getMonths() が返す月名は大文字では表示されません。


新たなHashSetの使用

LinkedHashSet は基本HashSet クラスのサブクラスです。したがってHashSet で行えることは、すべてLinkedHashSet で行うことができます。このクラスには新たに追加されたメソッドはありません。次の4つのコンストラクターがすべてです。

  • LinkedHashSet()
  • LinkedHashSet(Collection c)
  • LinkedHashSet(int initialCapacity)
  • LinkedHashSet(int initialCapacity, float loadFactor)

セットに要素を追加する場合、要素ごとにadd() を呼び出すことも、あるいはCollection を作成してこれをコンストラクターに渡すこともできます。ここでは配列に要素があるので、最も簡単な手段はArrays.asList() を利用することです。これは、元の配列順序を保ったまま、配列をラップしてList にして返します。コンストラクターにこのリストを渡すことによって、同じリストをLinkedHashSet と簡易HashSet の両方に容易に追加することができます。


リスト2. セットの充てん
                
  List list = Arrays.asList(months);
  Set orderedSet = new LinkedHashSet(list);
  Set unorderedSet = new HashSet(list);

セットを充てんすると、リンクによるセットが要素の挿入順序を保持しているかどうかその要素について確かめて、その結果を標準のハッシュ・セットの場合と比較することができます。iterator() を通じてセットごとに一つ一つ繰り返すことも、あるいは本来この作業を行ってくれるtoString() メソッドを(暗黙的に)ただ呼び出すこともできます。


リスト3.セット結果の表示
                
  System.out.println("Ordered:   " + orderedSet);
  System.out.println("Unordered: " + unorderedSet);

順序付けられたセットは次のように表示されます。


リスト4. 順序付きセット結果の表示
                
Ordered:   [January, February, March, April, May, June, July, August, September, October, November, December, ]
            

一方、順序なしセットは次のように表示されます。


リスト5. 順序なしセット結果の表示
                
Unordered: [March, April, November, October, January, July, September, February, December, May, June, August, ]


新たなHashMapの使用

LinkedHashMap は、基本的にはLinkedHashSet と同様に機能しますが、要素ごとにキーと値が必要になります。これはさらに、オリジナル・クラス(この場合HashMap)からサブクラス化しますが、コンストラクターは5つになります。

  • LinkedHashMap()
  • LinkedHashMap(int initialCapacity)
  • LinkedHashMap(int initialCapacity, float loadFactor)
  • LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
  • LinkedHashMap(Map)

新たに加わったコンストラクターは、アクセス順序のオプションを扱います。アクセス順序のデフォルトは、falseです。リストをアクセス順にするためには、true を渡す必要があります。つまりマップの先頭は、マップ・エントリーのうち最も前に使われたものになります。

通常のHashMap に比べてLinkedHashMap が優れている点は、繰り返しの回数がマップの容量に影響されないことです。大きい容量を選択した場合、通常のHashMap ではパフォーマンスに影響しますが、LinkedHashMap では繰り返し横断回数にまったく影響しません。

要素のペアごとにput() を行う必要があるという理由から、マップへの要素の追加は、セットに追加する場合よりもいくぶん複雑になります。以下のコードには特別なものはなく、Map を単にコンストラクターに渡してしまう代わりに、月名をループさせているだけです。


リスト6.マップの充てん
                
  Map orderedMap = new LinkedHashMap();
  Map unorderedMap = new HashMap();
  for (int i=0, n=months.length; i < n; i++) {
    orderedMap.put(months[i], italianMonths[i]);
    unorderedMap.put(months[i], italianMonths[i]);
  }

ありがたいことに、セットと同様に、マップのtoString() メソッドを呼び出すだけで挿入順にマップ・エントリーを取り出すことができます。これはキーと値の各ペアをkey=value として返します。


リスト7.マップ結果の表示
                
  System.out.println("Ordered:   " + orderedMap);
  System.out.println("Unordered: " + unorderedMap);
            

順序付けられたマップは次のように表示されます。


リスト8. 順序付きマップ結果の表示
                
Ordered:   {January=gennaio, February=febbraio, March=marzo, April=aprile, May=maggio,
June=giugno, July=luglio, August=agosto, September=settembre, October=ottobre, November=novembre,
December=dicembre, =}
            

一方、順序なしマップは次のように表示されます。


リスト9. 順序なしマップ結果の表示
                
Unordered: {August=agosto, July=luglio, November=novembre, June=giugno, October=ottobre,
April=aprile, May=maggio, March=marzo, January=gennaio,
  February=febbraio, =, December=dicembre, September=settembre}

自分でチェックするために、繰り返すことができます。反復子はすべて挿入順序を認識するので、values反復子 (values())を受け取った場合、次のように挿入順に値を確認することができます。


リスト10. 値の繰り返し
                
  Collection values = orderedMap.values();
  for (Iterator i = values.iterator(); i.hasNext(); System.out.println(i.next()));

これを表示すると、次のようになります。


リスト11. 順序付き値の表示
                
gennaio
febbraio
marzo
aprile
maggio
giugno
luglio
agosto
settembre
ottobre
novembre
dicembre


アクセス順に検査

新たなクラスの特徴の最後として、LinkedHashMap のアクセス順序のオプションを調べてみましょう。trueLinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) コンストラクターに渡すことによって、最も古く使用したものから直近に使用したものへと、アクセス順にマップのリンクによるリストを保持できるようになります。言い換えれば、新しいアイテムは最後に追加され、マップへの検索がアイテムをリンクによるリストの最後に移動するということです。この最後の点が重要です。通常のマップの読み取りアクセスが順序を変えるので、複数のスレッドがマップからの読み取りを行うような場合は、アクセスを同期化する必要があります。

では実際に月をいくつか検索して、新たな順序を出力してみましょう。


リスト12. アクセス順に要素を検査
                
  Map accessorderedMap = new LinkedHashMap(20, .80f, true);
  for (int i=0, n=months.length; i < n; i++) {
    accessorderedMap.put(months[i], italianMonths[i]);
  }
  accessorderedMap.get("June");
  accessorderedMap.get("April");
  accessorderedMap.get("February");
  System.out.println(accessorderedMap);

3つの月にアクセスしたので、これらはリストでDecemberの後の終わりの部分に移動し、Februaryを最後にしてアクセス順に並びます。


リスト13. アクセス順序結果の表示
                
{January=gennaio, March=marzo, May=maggio, July=luglio, August=agosto, September=settembre,
October=ottobre, November=novembre, December=dicembre, =, June=giugno, April=aprile,
February=febbraio}

注:リンクしたマップに余分な要素があることにお気付きでしょう。=は、getMethods() によって返される特別なエントリーです。何らかの理由でgetMonths() は、12ではなく13の月を返し、最後の月には名前がありません。同様の理由から、リンクによるセットの例には最後の要素がないのにコンマがあります。

追加の注:LinkedHashMap の1.4ベータ2バージョンは、プロテクトされたremoveEldestEntry() メソッドを追加します。サブクラスは、たとえばマップがn 個の要素を超えないようにするために最も古いノードを削除すべきであれば、このメソッドにtrueを返させることができます。


完結した例

それではソースの完結した例を紹介します。


リスト14. 完結した例
                
import java.util.*;
import java.text.*;
public class OrderedAccess {
  public static void main(String args[]) {
    String months[] = new DateFormatSymbols().getMonths();
    String italianMonths[] = new DateFormatSymbols(Locale.ITALIAN).getMonths();
    List list = Arrays.asList(months);
    Set orderedSet = new LinkedHashSet(list);
    Set unorderedSet = new HashSet(list);
    System.out.println("Ordered:   " + orderedSet);
    System.out.println("Unordered: " + unorderedSet);
    Map orderedMap = new LinkedHashMap();
    Map unorderedMap = new HashMap();
    for (int i=0, n=months.length; i < n; i++) {
      orderedMap.put(months[i], italianMonths[i]);
      unorderedMap.put(months[i], italianMonths[i]);
    }
    System.out.println("Ordered:   " + orderedMap);
    System.out.println("Unordered: " + unorderedMap);
    Collection values = orderedMap.values();
    for (Iterator i = values.iterator(); i.hasNext(); System.out.println(i.next()));
    Map accessorderedMap = new LinkedHashMap(20, .80f, true);
    for (int i=0, n=months.length; i < n; i++) {
      accessorderedMap.put(months[i], italianMonths[i]);
    }
    accessorderedMap.get("June");
    accessorderedMap.get("April");
    accessorderedMap.get("February");
    System.out.println(accessorderedMap);
  }
}
            


参考文献

著者について

Author photo

John Zukowskiは、JZ Ventures, Inc.の戦略的Javaコンサルティングを推進し、数多くのjGuruのコミュニティー主導のJava FAQsで常任指導者として活躍しています。最新の著書には、Apressから出版された「 Java Collections」および「 Definitive Guide to Swing for Java 2」 (第2版) があります。彼のメール・アドレスはjaz@zukowski.net です。

不正使用の報告のヘルプ

不正使用の報告

ありがとうございます。 このエントリーは、モデレーターの注目フラグが設定されました。


不正使用の報告のヘルプ

不正使用の報告

不正使用の報告の送信に失敗しました。


developerWorks: サイン・イン


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。 プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。 お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

表示名をお選びください

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

(半角英数字で3文字以上31文字以下にする必要があります)


「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


この記事を評価する

コメント

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=218834
ArticleTitle=Merlinの魔術: 挿入順序を保持する
publish-date=08012001
author1-email=jaz@zukowski.net
author1-email-cc=jaloi@us.ibm.com

タグ

Help
このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。

スライダーバーを使用することで、より多く(少なく)タグを表示します。

人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。

マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。

このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。