目次
全体紹介
方法剖析
get()
put()
remove()
HashSet
ホームページ Java &#&チュートリアル JavaコレクションフレームワークHashSetとHashMapのソースコードを詳細に分析(写真)

JavaコレクションフレームワークHashSetとHashMapのソースコードを詳細に分析(写真)

Mar 28, 2017 am 11:00 AM

全体紹介

HashSetHashMapを一緒に説明するのは、Javaでの実装は同じで、前者は後者をラップするだけなので、HashSet(アダプターモード)があるからです。 ) の中。したがって、この記事ではHashMapの分析に焦点を当てます。

HashMap

Map インターフェースを実装し、null 要素を配置できるようにします。このクラスが同期を実装していない点を除けば、残りは Hashtable とほぼ同じです。 TreeMap とは異なり、このコンテナは要素の順序を保証しません。コンテナは必要に応じて要素を再ハッシュする可能性があり、要素の順序も再シャッフルされるため、同じ null元素,除该类未实现同步外,其余跟Hashtable大致相同,跟TreeMap不同,该容器不保证元素顺序,根据需要该容器可能会对元素重新哈希,元素的顺序也会被重新打散,因此不同时间迭代同一个HashMap的顺序可能会不同。

根据对冲突的处理方式不同,哈希表有两种实现方式,一种开放地址方式(Open addressing),另一种是冲突链表方式(Separate chaining with linked lists)。Java HashMap采用的是冲突链表方式

JavaコレクションフレームワークHashSetとHashMapのソースコードを詳細に分析(写真)

从上图容易看出,如果选择合适的哈希函数put()get()方法可以在常数时间内完成。但在对HashMap进行迭代时,需要遍历整个table以及后面跟的冲突链表。因此对于迭代比较频繁的场景,不宜将HashMap的初始大小设的过大。

有两个参数可以影响HashMap的性能:初始容量(inital capacity)和负载系数(load factor)。初始容量指定了初始table的大小,负载系数用来指定自动扩容的临界值。当entry的数量超过capacity*load_factor时,容器将自动扩容并重新哈希。对于插入元素较多的场景,将初始容量设大可以减少重新哈希的次数。

将对向放入到HashMapHashSet中时,有两个方法需要特别关心:hashCode()equals()hashCode()方法决定了对象会被放到哪个bucket里,当多个对象的哈希值冲突时,equals()方法决定了这些对象是否是“同一个对象”。所以,如果要将自定义的对象放入到HashMapHashSet中,需要@Override hashCode()equals()方法。

方法剖析

get()

get(<a href="http://www.php.cn/wiki/60.html" target="_blank">Object</a> <a href="http://www.php.cn/wiki/1051.html" target="_blank">key</a>)方法根据指定的key值返回对应的value,该方法调用了getEntry(Object key)得到相应的entry,然后返回entry.getValue()。因此getEntry()是算法的核心。

算法思想是首先通过hash()函数得到对应bucket的下标,然后依次遍历冲突链表,通过key.equals(k)方法来判断是否是要找的那个entry

JavaコレクションフレームワークHashSetとHashMapのソースコードを詳細に分析(写真)

上图中hash(k)&(table.length-1)等价于hash(k)%table.length,原因是HashMap要求table.length必须是2的指数,因此table.length-1就是二进制低位全是1,跟hash(k)相与会将哈希值的高位全抹掉,剩下的就是余数了。

//getEntry()方法
final Entry<K,V> getEntry(Object key) {
    ......
    int hash = (key == null) ? 0 : hash(key);
    for (Entry<K,V> e = table[hash&(table.length-1)];//得到冲突链表
         e != null; e = e.next) {//依次遍历冲突链表中的每个entry
        Object k;
        //依据equals()方法判断是否相等
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k))))
            return e;
    }
    return null;
}
ログイン後にコピー

put()

put(K key, V value)方法是将指定的key, value对添加到map里。该方法首先会对map做一次查找,看是否包含该元组,如果已经包含则直接返回,查找过程类似于getEntry()方法;如果没有找到,则会通过addEntry(int hash, K key, V value, int bucketIndex)方法插入新的entry,插入方式为头插法

JavaコレクションフレームワークHashSetとHashMapのソースコードを詳細に分析(写真)

//addEntry()
void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {
        resize(2 * table.length);//自动扩容,并重新哈希
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = hash & (table.length-1);//hash%table.length
    }
    //在冲突链表头部插入新的entry
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}
ログイン後にコピー

remove()

remove(Object key)的作用是删除key值对应的entry,该方法的具体逻辑是在removeEntryForKey(Object key)里实现的。removeEntryForKey()方法会首先找到key值对应的entry,然后删除该entry(修改链表的相应指针)。查找过程跟getEntry()HashMap を反復する順序も変わります。

時期によっては異なる場合があります。 🎜🎜競合を処理するさまざまな方法に応じて、ハッシュ テーブルを実装するには 2 つの方法があります。1 つはオープン アドレッシング方式 (オープン アドレッシング)、もう 1 つは競合リンク リスト方式 (リンクされた リスト)。 🎜Java🎜HashMap🎜は競合リンクリスト方式🎜を採用しています。 🎜🎜JavaコレクションフレームワークHashSetとHashMapのソースコードを詳細に分析(写真) 🎜🎜上の図から、適切なハッシュを選択すると関数が実行されることが簡単にわかります。 , put() メソッドと get() メソッドは一定時間で完了できます。ただし、🎜HashMap🎜 を反復処理する場合は、テーブル全体とそれに続く競合リンク リストを走査する必要があります。したがって、反復が頻繁に行われるシナリオでは、🎜HashMap🎜 の初期サイズを大きすぎるように設定することは適切ではありません。 🎜🎜🎜HashMap🎜のパフォーマンスに影響を与える可能性のあるパラメータは 2 つあります: 初期容量と負荷率です。初期容量は初期の table サイズを指定し、負荷係数は自動拡張の重要な値を指定するために使用されます。 entry の数が capacity*load_factor を超えると、コンテナは自動的に拡張され、再ハッシュされます。多数の要素が挿入されるシナリオの場合、より大きな初期容量を設定すると、再ハッシュの数を減らすことができます。 🎜🎜オブジェクトを 🎜HashMap🎜 または 🎜HashSet🎜 に入れる場合、特別な注意が必要な 2 つのメソッド、hashCode()equals() があります。 🎜hashCode() メソッドは、オブジェクト が配置される場所を決定します。どの bucket 内で、複数のオブジェクトのハッシュ値が競合する場合、equals() メソッドはそれらのオブジェクトが「同じオブジェクト」であるかどうかを判断します🎜。したがって、カスタム オブジェクトを HashMap または HashSet に配置する場合は、 @Override hashCode()equals( ) が必要です。 メソッド。 🎜🎜メソッド分析🎜

get()

🎜get(<a href="http://www.php.cn/wiki/60.html" target="_blank">Object< /a> <a href="http://www.php.cn/wiki/1051.html" target="_blank">key</a>)メソッドは指定された に基づいています。 key</ code> 値は、対応する <code>value を返します。このメソッドは、getEntry(Object key) を呼び出して、対応する entry を取得してから返します。 エントリ。getValue()。したがって、getEntry() がアルゴリズムの中核となります。 🎜🎜アルゴリズムのアイデアは、まず hash() 関数を通じて対応する bucket の添字を取得し、次に競合リンク リストを順番に走査し、 を渡すことです。 >key.equals(k)</ code> メソッドを使用して、それが探している <code>entry であるかどうかを判断します。 🎜🎜JavaコレクションフレームワークHashSetとHashMapのソースコードを詳細に分析(写真) 🎜🎜 上の図では、 hash(k)&(table.length-1)hash(k)%table.length と同等であるためです。 🎜 HashMap🎜 では、table.length は 2 の指数である必要があるため、table.length-1 はバイナリの下位ビットがすべて 1 であり、 との AND であることを意味します。 hash(k) は、ハッシュ値の上位ビットをすべて消去し、残りが残ります。 🎜
//removeEntryForKey()
final Entry<K,V> removeEntryForKey(Object key) {
    ......
    int hash = (key == null) ? 0 : hash(key);
    int i = indexFor(hash, table.length);//hash&(table.length-1)
    Entry<K,V> prev = table[i];//得到冲突链表
    Entry<K,V> e = prev;
    while (e != null) {//遍历冲突链表
        Entry<K,V> next = e.next;
        Object k;
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k)))) {//找到要删除的entry
            modCount++; size--;
            if (prev == e) table[i] = next;//删除的是冲突链表的第一个entry
            else prev.next = next;
            return e;
        }
        prev = e; e = next;
    }
    return e;
}
ログイン後にコピー
ログイン後にコピー

put()

🎜put(K key, V value) メソッドは、指定された key, value ペアを に追加します。 を内部にマップします。このメソッドは、まず map を検索して、タプルが含まれているかどうかを確認します。含まれている場合、検索プロセスは getEntry() メソッドと似ています。見つからない場合は、新しい entryaddEntry(int hash, K key, V value, intbucketIndex) メソッドによって挿入されます。挿入メソッドは 🎜ヘッドの挿入方法🎜。 🎜🎜JavaコレクションフレームワークHashSetとHashMapのソースコードを詳細に分析(写真) 🎜
//HashSet是对HashMap的简单包装
public class HashSet<E>
{
    ......
    private transient HashMap<E,Object> map;//HashSet里面有一个HashMap
    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();
    public HashSet() {
        map = new HashMap<>();
    }
    ......
    public boolean add(E e) {//简单的方法转换
        return map.put(e, PRESENT)==null;
    }
    ......
}
ログイン後にコピー
ログイン後にコピー

remove()

🎜remove(Object key) は、key 値に対応する entry を削除するために使用されます。メソッドの特定のロジックは、removeEntryForKey(Object key) で実装されます。 removeEntryForKey() メソッドは、まず key 値に対応する entry を検索し、次に entry を削除します (対応するポインタを変更します)。検索プロセスは、getEntry() プロセスに似ています。 🎜

JavaコレクションフレームワークHashSetとHashMapのソースコードを詳細に分析(写真)

//removeEntryForKey()
final Entry<K,V> removeEntryForKey(Object key) {
    ......
    int hash = (key == null) ? 0 : hash(key);
    int i = indexFor(hash, table.length);//hash&(table.length-1)
    Entry<K,V> prev = table[i];//得到冲突链表
    Entry<K,V> e = prev;
    while (e != null) {//遍历冲突链表
        Entry<K,V> next = e.next;
        Object k;
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k)))) {//找到要删除的entry
            modCount++; size--;
            if (prev == e) table[i] = next;//删除的是冲突链表的第一个entry
            else prev.next = next;
            return e;
        }
        prev = e; e = next;
    }
    return e;
}
ログイン後にコピー
ログイン後にコピー

HashSet

前面已经说过HashSet是对HashMap的简单包装,对HashSet的函数调用都会转换成合适的HashMap方法,因此HashSet的实现非常简单,只有不到300行代码。这里不再赘述。

//HashSet是对HashMap的简单包装
public class HashSet<E>
{
    ......
    private transient HashMap<E,Object> map;//HashSet里面有一个HashMap
    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();
    public HashSet() {
        map = new HashMap<>();
    }
    ......
    public boolean add(E e) {//简单的方法转换
        return map.put(e, PRESENT)==null;
    }
    ......
}
ログイン後にコピー
ログイン後にコピー

以上がJavaコレクションフレームワークHashSetとHashMapのソースコードを詳細に分析(写真)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、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衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

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

SublimeText3 中国語版

SublimeText3 中国語版

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

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

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

Javaの完全数 Javaの完全数 Aug 30, 2024 pm 04:28 PM

Java における完全数のガイド。ここでは、定義、Java で完全数を確認する方法、コード実装の例について説明します。

ジャワのウェカ ジャワのウェカ Aug 30, 2024 pm 04:28 PM

Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

Javaのスミス番号 Javaのスミス番号 Aug 30, 2024 pm 04:28 PM

Java のスミス番号のガイド。ここでは定義、Java でスミス番号を確認する方法について説明します。コード実装の例。

Java Springのインタビューの質問 Java Springのインタビューの質問 Aug 30, 2024 pm 04:29 PM

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8 Stream Foreachから休憩または戻ってきますか? Java 8 Stream Foreachから休憩または戻ってきますか? Feb 07, 2025 pm 12:09 PM

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

Java での日付までのタイムスタンプ Java での日付までのタイムスタンプ Aug 30, 2024 pm 04:28 PM

Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

カプセルの量を見つけるためのJavaプログラム カプセルの量を見つけるためのJavaプログラム Feb 07, 2025 am 11:37 AM

カプセルは3次元の幾何学的図形で、両端にシリンダーと半球で構成されています。カプセルの体積は、シリンダーの体積と両端に半球の体積を追加することで計算できます。このチュートリアルでは、さまざまな方法を使用して、Javaの特定のカプセルの体積を計算する方法について説明します。 カプセルボリュームフォーミュラ カプセルボリュームの式は次のとおりです。 カプセル体積=円筒形の体積2つの半球体積 で、 R:半球の半径。 H:シリンダーの高さ(半球を除く)。 例1 入力 RADIUS = 5ユニット 高さ= 10単位 出力 ボリューム= 1570.8立方ユニット 説明する 式を使用してボリュームを計算します。 ボリューム=π×R2×H(4

Spring Tool Suiteで最初のSpring Bootアプリケーションを実行するにはどうすればよいですか? Spring Tool Suiteで最初のSpring Bootアプリケーションを実行するにはどうすればよいですか? Feb 07, 2025 pm 12:11 PM

Spring Bootは、Java開発に革命をもたらす堅牢でスケーラブルな、生産対応のJavaアプリケーションの作成を簡素化します。 スプリングエコシステムに固有の「構成に関する慣習」アプローチは、手動のセットアップを最小化します。

See all articles