ホームページ Java &#&チュートリアル Java の HashMap と TreeMap の違いを深く理解する

Java の HashMap と TreeMap の違いを深く理解する

Jan 19, 2017 am 10:56 AM

まず、Mapとは何かをご紹介します。配列では、配列の添字を使用してその内容にインデックスを付けますが、マップでは、オブジェクトを使用してオブジェクトにインデックスを付けます。インデックスに使用されるオブジェクトはキーと呼ばれ、それに対応するオブジェクトは値と呼ばれます。これは通常、キーと値のペアと呼ばれるものです。

HashMap はハッシュコードを使用してコンテンツを迅速に検索しますが、TreeMap 内のすべての要素は特定の固定順序を維持します。順序付けされた結果を取得する必要がある場合は、TreeMap を使用する必要があります (HashMap 内の要素の順序は安定していません)。
HashMap 非スレッドセーフ TreeMap 非スレッドセーフ

スレッドセーフ
Java では、スレッド セーフは通常、次の 2 つの側面に反映されます。
1. 同じ Java インスタンスへの複数のスレッドのアクセス (読み取りと変更) は行われません。それは主にキーワードの同期に反映されます。 ArrayList と Vector、HashMap と Hashtable
など (後者には各メソッドの前に synchronized キーワードがあります)。 List オブジェクトを操作していて、他のスレッドが要素を削除している場合、問題が発生します。

2. 各スレッドには独自のフィールドがあり、複数のスレッド間で共有されません。これは主に java.lang.ThreadLocal クラスに反映されますが、static や transient などの Java キーワードはサポートされていません。
1.AbstractMap 抽象クラスと SortedMap インターフェイス
AbstractMap 抽象クラス: (HashMap は AbstractMap を継承) 2 つの等しいマップが同じハッシュ コードを返すように、equals() メソッドと hashCode() メソッドをオーバーライドします。 2 つのマップは、サイズが等しく、同じキーが含まれ、両方のマップで各キーが同じ値を持つ場合、等しいと見なされます。マップのハッシュ コードは、マップの要素のハッシュ コードの合計であり、各要素は Map.Entry インターフェイスの実装です。したがって、2 つの等しいマッピングは、マッピングの内部順序に関係なく、同じハッシュ コードを報告します。
SortedMap インターフェース: (TreeMap は SortedMap から継承) キーの順序を維持するために使用されます。 SortedMap インターフェイスは、2 つのエンドポイントを含む画像のビュー (サブセット) へのアクセス メソッドを提供します。 SortedMap の処理は、ソートがマップのキーに対して実行されることを除いて、SortedSet の処理と同じです。 SortedMap 実装クラスに追加される要素は Comparable インターフェイスを実装する必要があります。それ以外の場合は、そのコンストラクターに Comparator インターフェイスの実装を提供する必要があります。 TreeMap クラスはその唯一の実装です。

2. 2 つの従来の Map 実装
HashMap: ハッシュ テーブル実装に基づいています。 HashMap を使用するには、追加されたキー クラスで hashCode() とquals() を明確に定義する必要があります [hashCode() とquals() はオーバーライドできます]。HashMap スペースの使用を最適化するために、初期容量と負荷係数を調整できます。
(1)HashMap(): 空のハッシュ イメージを構築します
(2)HashMap(Map m): ハッシュ イメージを構築し、イメージ m のすべてのマッピングを追加します
(3)HashMap(intInitialCapacity): 空のハッシュ イメージを構築します特定の容量を持つ
(4)HashMap(intInitialCapacity, floatloadFactor): 特定の容量と負荷係数を持つ空のハッシュ イメージを構築します
TreeMap: 赤黒ツリー実装に基づきます。ツリーは常にバランスが保たれているため、TreeMap には調整オプションはありません。
(1)TreeMap(): 空のイメージ ツリーを構築します
(2)TreeMap(Map m): イメージ ツリーを構築し、イメージ m 内のすべての要素を追加します
(3)TreeMap(Comparator c): イメージ ツリーを構築し、そして特定のコンパレータを使用してキーワードを並べ替えます
(4)TreeMap(SortedMap s): 画像ツリーを構築し、すべてのマッピングを画像ツリーに追加し、順序付けされた画像 s と同じコンパレータを使用して並べ替えます

3 。2 つの一般的なマッププロパティ
HashMap: Map 内の要素の挿入、削除、配置に適しています。
ツリーマップ: 自然な順序またはカスタム順序でキーを移動するのに適しています。

4. 概要
HashMap は通常、TreeMap よりも少し高速です (ツリーとハッシュ テーブルのデータ構造のため)。ソートされたマップが必要な場合にのみ、HashMap を使用することをお勧めします。

import java.util.HashMap; 
import java.util.Hashtable; 
import java.util.Iterator; 
import java.util.Map; 
import java.util.TreeMap; 
public class HashMaps { 
public static void main(String[] args) { 
Map<String, String> map = new HashMap<String, String>(); 
map.put("a", "aaa"); 
map.put("b", "bbb"); 
map.put("c", "ccc"); 
map.put("d", "ddd"); 
Iterator<String> iterator = map.keySet().iterator(); 
while (iterator.hasNext()) { 
Object key = iterator.next(); 
System.out.println("map.get(key) is :" + map.get(key)); 
} 
// 定义HashTable,用来测试 
Hashtable<String, String> tab = new Hashtable<String, String>(); 
tab.put("a", "aaa"); 
tab.put("b", "bbb"); 
tab.put("c", "ccc"); 
tab.put("d", "ddd"); 
Iterator<String> iterator_1 = tab.keySet().iterator(); 
while (iterator_1.hasNext()) { 
Object key = iterator_1.next(); 
System.out.println("tab.get(key) is :" + tab.get(key)); 
} 
TreeMap<String, String> tmp = new TreeMap<String, String>(); 
tmp.put("a", "aaa"); 
tmp.put("b", "bbb"); 
tmp.put("c", "ccc"); 
tmp.put("d", "cdc"); 
Iterator<String> iterator_2 = tmp.keySet().iterator(); 
while (iterator_2.hasNext()) { 
Object key = iterator_2.next(); 
System.out.println("tmp.get(key) is :" + tmp.get(key)); 
} 
} 
}
ログイン後にコピー

実行結果は以下の通りです:
map.get(key) は :ddd
map.get(key) は :bbb
map.get(key) は :ccc
map.get(key) は :aaa
tab.get(key) は :bbb
tab.get(key) は :aaa
tab.get(key) は :ddd
tab.get(key) は :ccc
tmp.get(key) は :aaa
tmp.get(key) は :bbb
tmp.get(key) は :ccc
tmp.get(key) は :cdc
TreeMap が出力した結果はソートされますが、HashMap の結果はソートされません。
それでは、この記事の本題に入りましょう。まず、HashMap の使用方法を説明するための例を示します。

import java.util.*; 
public class Exp1 { 
public static void main(String[] args){ 
HashMap h1=new HashMap(); 
Random r1=new Random(); 
for (int i=0;i<1000;i++){ 
Integer t=new Integer(r1.nextInt(20)); 
if (h1.containsKey(t)) 
((Ctime)h1.get(t)).count++; 
else 
h1.put(t, new Ctime()); 
} 
System.out.println(h1); 
} 
} 
class Ctime{ 
int count=1; 
public String toString(){ 
return Integer.toString(count); 
} 
}
ログイン後にコピー

HashMap では、get() を通じて値を取得し、put() を通じて値を挿入し、ContainsKey() によってオブジェクトが既に存在するかどうかを確認します。 ArrayList の操作と比較すると、キーによるコンテンツのインデックス付けを除けば、HashMap は他の側面で大きな違いがないことがわかります。
前に紹介したように、HashMap は HashCode に基づいています。すべてのオブジェクトのスーパークラス Object に HashCode() メソッドがありますが、equals メソッドと同様にすべての状況に適用できるわけではないため、独自の HashCode を書き直す必要があります。 ()方法。以下に例を示します:

import java.util.*; 
public class Exp2 { 
public static void main(String[] args){ 
HashMap h2=new HashMap(); 
for (int i=0;i<10;i++) 
h2.put(new Element(i), new Figureout()); 
System.out.println("h2:"); 
System.out.println("Get the result for Element:"); 
Element test=new Element(5); 
if (h2.containsKey(test)) 
System.out.println((Figureout)h2.get(test)); 
else 
System.out.println("Not found"); 
} 
} 
class Element{ 
int number; 
public Element(int n){ 
number=n; 
} 
} 
class Figureout{ 
Random r=new Random(); 
boolean possible=r.nextDouble()>0.5; 
public String toString(){ 
if (possible) 
return "OK!"; 
else 
return "Impossible!"; 
} 
}
ログイン後にコピー

在这个例子中,Element用来索引对象Figureout,也即Element为key,Figureout为value。在Figureout中随机生成一个浮点数,如果它比0.5大,打印"OK!",否则打印"Impossible!"。之后查看Element(3)对应的Figureout结果如何。

结果却发现,无论你运行多少次,得到的结果都是"Not found"。也就是说索引Element(3)并不在HashMap中。这怎么可能呢?
原因得慢慢来说:Element的HashCode方法继承自Object,而Object中的HashCode方法返回的HashCode对应于当前的地址,也就是说对于不同的对象,即使它们的内容完全相同,用HashCode()返回的值也会不同。这样实际上违背了我们的意图。因为我们在使用 HashMap时,希望利用相同内容的对象索引得到相同的目标对象,这就需要HashCode()在此时能够返回相同的值。在上面的例子中,我们期望 new Element(i) (i=5)与 Elementtest=newElement(5)是相同的,而实际上这是两个不同的对象,尽管它们的内容相同,但它们在内存中的地址不同。因此很自然的,上面的程序得不到我们设想的结果。下面对Element类更改如下:

class Element{ 
int number; 
public Element(int n){ 
number=n; 
} 
public int hashCode(){ 
return number; 
} 
public boolean equals(Object o){ 
return (o instanceof Element) && (number==((Element)o).number); 
} 
}
ログイン後にコピー

在这里Element覆盖了Object中的hashCode()和equals()方法。覆盖hashCode()使其以number的值作为 hashcode返回,这样对于相同内容的对象来说它们的hashcode也就相同了。而覆盖equals()是为了在HashMap判断两个key是否相等时使结果有意义(有关重写equals()的内容可以参考我的另一篇文章《重新编写Object类中的方法》)。修改后的程序运行结果如下: 
h2: 
Get the result for Element: 
Impossible! 
请记住:如果你想有效的使用HashMap,你就必须重写在其的HashCode()。 
还有两条重写HashCode()的原则: 
[list=1] 
不必对每个不同的对象都产生一个唯一的hashcode,只要你的HashCode方法使get()能够得到put()放进去的内容就可以了。即"不为一原则"。 

生成hashcode的算法尽量使hashcode的值分散一些,不要很多hashcode都集中在一个范围内,这样有利于提高HashMap的性能。即"分散原则"。至于第二条原则的具体原因,有兴趣者可以参考Bruce Eckel的《Thinking in Java》,在那里有对HashMap内部实现原理的介绍,这里就不赘述了。 
掌握了这两条原则,你就能够用好HashMap编写自己的程序了。不知道大家注意没有,java.lang.Object中提供的三个方法:clone(),equals()和hashCode()虽然很典型,但在很多情况下都不能够适用,它们只是简单的由对象的地址得出结果。这就需要我们在自己的程序中重写它们,其实java类库中也重写了千千万万个这样的方法。利用面向对象的多态性——覆盖,Java的设计者很优雅的构建了Java的结构,也更加体现了Java是一门纯OOP语言的特性。

更多Java中HashMap和TreeMap的区别深入理解相关文章请关注PHP中文网!


このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

会社のセキュリティソフトウェアはアプリケーションの実行に失敗していますか?それをトラブルシューティングと解決する方法は? 会社のセキュリティソフトウェアはアプリケーションの実行に失敗していますか?それをトラブルシューティングと解決する方法は? Apr 19, 2025 pm 04:51 PM

一部のアプリケーションが適切に機能しないようにする会社のセキュリティソフトウェアのトラブルシューティングとソリューション。多くの企業は、内部ネットワークセキュリティを確保するためにセキュリティソフトウェアを展開します。 ...

エンティティクラス変数名をエレガントに取得して、データベースクエリ条件を構築する方法は? エンティティクラス変数名をエレガントに取得して、データベースクエリ条件を構築する方法は? Apr 19, 2025 pm 11:42 PM

データベース操作にMyBatis-Plusまたはその他のORMフレームワークを使用する場合、エンティティクラスの属性名に基づいてクエリ条件を構築する必要があることがよくあります。あなたが毎回手動で...

MapsTructを使用したシステムドッキングのフィールドマッピングの問題を簡素化する方法は? MapsTructを使用したシステムドッキングのフィールドマッピングの問題を簡素化する方法は? Apr 19, 2025 pm 06:21 PM

システムドッキングでのフィールドマッピング処理は、システムドッキングを実行する際に難しい問題に遭遇することがよくあります。システムのインターフェイスフィールドを効果的にマッピングする方法A ...

名前を数値に変換してソートを実装し、グループの一貫性を維持するにはどうすればよいですか? 名前を数値に変換してソートを実装し、グループの一貫性を維持するにはどうすればよいですか? Apr 19, 2025 pm 11:30 PM

多くのアプリケーションシナリオでソートを実装するために名前を数値に変換するソリューションでは、ユーザーはグループ、特に1つでソートする必要がある場合があります...

Intellijのアイデアは、ログを出力せずにSpring Bootプロジェクトのポート番号をどのように識別しますか? Intellijのアイデアは、ログを出力せずにSpring Bootプロジェクトのポート番号をどのように識別しますか? Apr 19, 2025 pm 11:45 PM

intellijideaultimatiateバージョンを使用してスプリングを開始します...

Javaオブジェクトを配列に安全に変換する方法は? Javaオブジェクトを配列に安全に変換する方法は? Apr 19, 2025 pm 11:33 PM

Javaオブジェクトと配列の変換:リスクの詳細な議論と鋳造タイプ変換の正しい方法多くのJava初心者は、オブジェクトのアレイへの変換に遭遇します...

eコマースプラットフォームSKUおよびSPUデータベースデザイン:ユーザー定義の属性と原因のない製品の両方を考慮する方法は? eコマースプラットフォームSKUおよびSPUデータベースデザイン:ユーザー定義の属性と原因のない製品の両方を考慮する方法は? Apr 19, 2025 pm 11:27 PM

eコマースプラットフォーム上のSKUおよびSPUテーブルの設計の詳細な説明この記事では、eコマースプラットフォームでのSKUとSPUのデータベース設計の問題、特にユーザー定義の販売を扱う方法について説明します。

データベースクエリにTKMYBATISを使用するときに、エンティティクラスの変数名の構築クエリ条件をエレガントに取得する方法は? データベースクエリにTKMYBATISを使用するときに、エンティティクラスの変数名の構築クエリ条件をエレガントに取得する方法は? Apr 19, 2025 pm 09:51 PM

データベースクエリにTKMYBATISを使用する場合、クエリ条件を構築するためにエンティティクラスの変数名を優雅に取得する方法は一般的な問題です。この記事はピン留めします...

See all articles