Java 面接の質問集 (回答付き)

目次

TCPとUDPの違い

取得と投稿の違い

Cookieとセッションの違い

Java の基本的なタイプは何ですか?

抽象クラスとインターフェイスの違いは何ですか? 

スタックを理解する

== と等しいの違い

Java ポリモーフィズムを理解するにはどうすればよいですか?

スレッドを作成するにはどのような方法がありますか?

ダーティ リード、非再現性、ファントム リードとは何ですか?

Java のガベージ コレクション メカニズム

TCP ではハンドシェイクが 2 回ではなく 3 回必要なのはなぜですか?

ハッシュマップの拡張率が 0.75 である理由

hashmap1.7と1.8の展開機構の違い

同時ハッシュマップの導入


TCPとUDPの違い

TCP (Transmission Control Protocol) と UDP (User Datagram Protocol) は、コンピュータ ネットワークでデータを送信するために使用される 2 つの異なるトランスポート層プロトコルです。それらの主な違いは次のとおりです。

  1. 接続性:

    • TCP は、データ送信前に接続を確立してデータの信頼性と順序性を保証する接続指向のプロトコルです。接続の確立と終了には 3 ウェイ ハンドシェイクと 4 つのウェーブが使用されます。
    • UDP はコネクションレス型プロトコルであり、接続を確立せずにデータ パケットを直接送信できます。これは、UDP の転送速度が速いことを意味しますが、データの信頼性と順序は保証されません。
  2. 信頼性:

    • TCP は信頼性の高いデータ送信を提供し、データが確実に宛先に到達し、必要に応じて再送信されます。データが失われたり破損した場合、TCP は自動的にデータを回復します。
    • UDP は信頼性の保証を提供せず、データ パケットが失われたり、異なる順序で到着したりする可能性があり、アプリケーションはデータの整合性とシーケンス自体を処理する必要があります。
  3. 該当するシーン:

    • TCP は通常、Web ブラウジング、電子メール、ファイル転送など、信頼性の高いデータ送信が必要なアプリケーションや、高いデータ整合性が必要なシナリオで使用されます。
    • UDP は、オーディオやビデオのストリーミング、オンライン ゲーム、低遅延を必要とするアプリケーションなどのリアルタイム アプリケーションによく使用されます。これにより、データをより高速に転送できますが、ある程度のデータ損失は許容できます。
  4. ヘッダーのオーバーヘッド:

    • TCP ヘッダーはより大きく、多くの制御情報が含まれるため、より多くの帯域幅を消費します。
    • UDP はヘッダーが小さいため、占有帯域幅が少なく、帯域幅が制限されている状況に適しています。

つまり、TCP と UDP はさまざまなネットワーク アプリケーション シナリオに適しており、どのプロトコルを選択するかは、信頼性の高い伝送が必要か、より高速な伝送速度が必要かなど、アプリケーションの要件によって異なります。

より大きな TCP ヘッダー: TCP プロトコルには、各データ パケットに比較的大量の制御情報が含まれており、信頼性の高いデータ送信を確保するために TCP 接続を管理および維持するために使用されます。この制御情報には、シーケンス番号、確認番号、ウィンドウ サイズ、チェックサムなどのフィールドが含まれます。この追加情報により、TCP パケットのヘッダー サイズが大きくなり、送信帯域幅の一部を占有します。

取得と投稿の違い

HTTP (ハイパーテキスト転送プロトコル) の GET と POST は、クライアントとサーバーの間でデータを転送するためによく使用される 2 つのリクエスト メソッドです。それらの主な違いは次のとおりです。

  1. データ送信方法:

    • GET: URL を通じてリクエストにデータを添付します。データはキーと値のペアの形式で URL のクエリ文字列に表示されます。したがって、GET リクエストは URL 内のデータを公開し、表示され、簡単にキャッシュされたり、ブックマークされたりすることになります。
    • POST:データを URL で公開するのではなく、リクエストの本文に含めます。これにより、データが URL に表示されなくなるため、POST リクエストは機密データや大量のデータの送信により適したものになります。
  2. データサイズ制限:

    • GET:データが URL に追加されるため、GET リクエストには送信データのサイズに制限があり、通常はブラウザーとサーバーによって制限され、データ送信のセキュリティは低くなります。
    • POST:データはリクエスト本文に含まれているため、POST リクエストのデータ サイズ制限は比較的高く、通常は大量のデータを転送できます。
  3. 安全性:

    • GET:データは URL で公開されるため、GET リクエストは安全性が低く、機密性のないデータの送信に適しています。
    • POST: POST リクエストにはリクエスト本文にデータが含まれるため、安全性が高く、ログイン資格情報や支払い情報などの機密データの送信に適しています。
  4. キャッシュ可能性:

    • GET: GET リクエストは冪等であるため、通常、ブラウザによってキャッシュできます。つまり、複数の同一の GET リクエストは同じ結果を生成するはずです。
    • POST: POST リクエストは、データが変更されたり、異なる結果が生成されたりする可能性があるため、通常、ブラウザーによってキャッシュされません。
  5. 冪等性:

    • GET: GET リクエストはべき等であり、複数の同一の GET リクエストによってサーバーの状態やデータが変更されるべきではありません。
    • POST: POST リクエストは通常​​冪等ではなく、複数の同一の POST リクエストによりサーバーの状態が変化したり、同じデータが複数回送信されたりする可能性があります。

つまり、HTTP 通信には GET と POST の両方が使用されますが、データ送信方法、データ サイズの制限、セキュリティ、キャッシュ可能性、冪等性の点で異なる特性があり、特定のニーズに基づいてどちらのリクエストを使用するかを選択する必要があります。GET はデータの取得に適しており、POST はデータの送信とデータの変更に適しています。

Cookieとセッションの違い

Cookie とセッションはどちらも、Web アプリケーションでユーザー状態を追跡し、セッション情報を維持するために使用されるメカニズムですが、これらの間にはいくつかの重要な違いがあります。

  1. ストレージの場所:

    • Cookie: Cookie はクライアント (ユーザーのブラウザ) に保存される小さなテキスト ファイルで、サーバーによってクライアントに送信され、ローカルに保存されます。ブラウザがサーバーにリクエストを送信するたびに、サーバーがユーザーを識別できるように、関連する Cookie データがリクエスト ヘッダーに自動的に追加されます。
    • セッション:セッション データは通常、クライアントではなくサーバーに保存されます。サーバーはセッションごとに一意の識別子 (セッション ID) を作成します。これは通常 Cookie に保存されますが、実際のセッション データはサーバーに保存されます。
  2. データストレージ:

    • Cookie: Cookie には通常、少量のテキスト データが含まれており、ユーザー設定やログイン資格情報など、ユーザーに関する小さな情報を保存するために使用されます。Cookie のサイズは、ブラウザの Cookie サイズ制限によって制限されます。
    • セッション:セッション データはサーバーに保存され、Cookie サイズによって制限されないため、セッションにはより大きく複雑なデータを保存できます。通常、Session はユーザーのログイン ステータス、ショッピング カートの内容、ユーザー セッション情報などを保存するために使用されます。
  3. 安全性:

    • Cookie: Cookie はクライアント側に保存されるため、ユーザーは表示および変更できます。Cookie を安全および HttpOnly としてマークしてセキュリティを強化することはできますが、それでも盗難や悪用のリスクはあります。
    • セッション:セッション データはサーバーに保存されるため、ユーザーはセッション データを直接表示したり変更したりすることができないため、セキュリティが向上します。ただし、サーバー上のセッション データも、不正アクセスを防ぐために適切に保護する必要があります。
  4. ライフサイクル:

    • Cookie: Cookie の有効期限は、セッション レベル (ブラウザを閉じると期限切れになる) または永続的なもの (一定期間後に期限切れになる) に設定できます。
    • セッション:セッションは通常、ユーザーがブラウザを閉じた後、または一定期間非アクティブになった後に期限切れになります。特定の期限切れポリシーは開発者が制御できます。

    つまり、Cookie とセッションはどちらも Web アプリケーションでユーザーのステータスを管理し、セッション情報を維持するために使用されますが、保存場所、データ保存、セキュリティ、ライフサイクルなどの点で異なる特性があり、特定の条件に基づいて選択する必要があります。ニーズとセキュリティに関する考慮事項、適切なメカニズム。実際のアプリケーションでは、たとえば、サーバーがセッションを識別できるようにセッション ID を Cookie に保存するために、これらが一緒に使用されることがよくあります。

Java の基本的なタイプは何ですか?

Java の基本データ型はプリミティブ データ型とも呼ばれ、単一の値を格納するために使用されます。Java には次の基本的なデータ型があります。

  1. 整数型:

    • byte: 8 ビット、-128 ~ 127 の範囲。
    • short: 16 ビット、-32,768 ~ 32,767 の範囲。
    • int: 32 ビット、約 -21 億から 21 億の範囲。
    • long: 64 ビット、範囲は非常に大きく、約 -900 ~ 900 です。

    数値的には、Baijing は 10^23 (または 1 の後に 23 個のゼロが続く) として理解できます。

  2. 浮動小数点型:

    • float: 32 ビット。小数を格納するために使用され、有効数字約 6 ~ 7 桁の精度です。
    • double: 64 ビット。有効数字約 15 ~ 16 桁の倍精度 10 進数を格納するために使用されます。
  3. 文字タイプ:

    • char: 16 ビット。文字、数字、記号などの文字を格納するために使用されます。
  4. ブール型:

    • boolean: true または false の値を示します。

抽象クラスとインターフェイスの違いは何ですか? 

抽象クラスとインターフェイスは、ポリモーフィズムとコード抽象化を実現するために使用されるオブジェクト指向プログラミングにおける 2 つの異なる概念ですが、これらの間にはいくつかの重要な違いがあります。

  1. 定義方法:

    • 抽象クラス: 抽象クラスはabstractキーワードを使用して定義され、抽象メソッド (つまり、実際の実装がないメソッド) と具象メソッド (実際の実装のあるメソッド) を含めることができます。
    • インターフェイス: インターフェイスはinterfaceキーワードを使用して定義され、抽象メソッド (メソッド本体は含まれません) のみが含まれ、具体的なメソッドは含まれません。

    たとえば、次のような抽象クラスを定義できます

    学生に継承させて main メソッドで呼び出す

    実行結果は次のとおりです。

  2. 継承関係:

    • 抽象クラス: クラスは 1 つの抽象クラスからのみ継承できます。これは Java では単一継承です。抽象クラスには、メンバー変数、コンストラクター、および非抽象メソッドを含めることができます。
    • インターフェイス: クラスは複数のインターフェイス、つまり Java での複数のインターフェイス実装を実装できます。インターフェイスには抽象メソッドと定数フィールド (final変数) のみが含まれており、コンストラクターや具象メソッドは含まれません。
  3. 施工方法:

    • 抽象クラス: 抽象クラスのメンバー変数を初期化するコンストラクター メソッドを持つことができます。
    • インターフェイス: インターフェイスはインスタンス化できないため、コンストラクターを持つことはできません。
  4. メンバー変数:

    • 抽象クラス: インスタンス変数 (メンバー変数) を含めることができます。
    • インターフェイス: 定数フィールドのみを含めることができます。定数フィールドは一般にパブリック定数とみなされます。
  5. 実現方法:

    • 抽象クラス: 継承によって実装されるサブクラスは、extendsキーワードを使用して抽象クラスを拡張し、抽象クラスに抽象メソッドを実装する必要があります。
    • インターフェイス: 実装を通じて実装されるクラスは、implementsキーワードを使用してインターフェイスを実装し、インターフェイスで定義されているすべての抽象メソッドの具体的な実装を提供します。
  6. 使用:

    • 抽象クラス: 通常、共有コードとプロパティを含めることができる基本的な抽象概念を表すために使用されます。これらは、クラス階層内の基本クラスを確立するためによく使用されます。
    • インターフェイス: 実装クラスが特定の動作を提供することを必要とする一連のコントラクトを定義するために使用されます。インターフェイスはポリモーフィズムを実現するためによく使用され、クラスが複数のインターフェイスを実装できるようにします。

つまり、抽象クラスと抽象インターフェイスは、どちらも Java でコードの抽象化とポリモーフィズムを実現するための重要なメカニズムですが、その使用方法と構文は明らかに異なり、ニーズと設計目標に応じて適切な抽象化方法を選択する必要があります。一般に、共有コードとプロパティを表す必要がある場合は抽象クラスを使用し、一連の仕様または動作規約を定義する必要がある場合はインターフェイスを使用します。場合によっては、抽象クラスとインターフェイスを一緒に使用して、より複雑な継承構造を実装できます。

スタックを理解する

スタックは通常、「後入れ先出し」(LIFO) の原則に従います。つまり、スタックに入る最後の要素が最初に削除され、スタックに入る最初の要素が最後に削除されます。削除されました。

スタックの完全な説明は次のとおりです。

スタックは、データの保存と管理に使用される一般的なコンピューター サイエンス データ構造です。スタックは LIFO (後入れ先出し) 原則に従います。これは、スタックに入る最後の要素が最初に削除され、スタックに入る最初の要素が最後に削除されることを意味します。

スタックの基本的な操作には次のものがあります。

  1. プッシュ:要素がスタックに配置されると、その要素はスタックの先頭に追加され、スタックの新しい先頭要素になります。

  2. Pop:スタックの最上位要素が削除されると、その要素はスタックから削除され、次の要素がスタックの新しい最上位要素になります。

  3. View Top:スタックの最上位の要素を削除せずに表示できます。

  4. 空のスタック:スタックに要素がない場合、そのスタックは「空のスタック」と呼ばれます。

スタックの一般的なアプリケーションには、関数呼び出しスタック、式の評価、メモリ管理、バックトラッキング アルゴリズムなどがあります。

== と等しいの違い

Java では、==と はequals()オブジェクトを比較するために使用される 2 つの異なる方法であり、次のような違いがあります。

  1. オブジェクト タイプの比較:

    • ==2 つのオブジェクトの参照 (メモリ アドレス) を比較する、つまり 2 つのオブジェクトが同じオブジェクトであるかどうかを判断するために使用されます。
    • equals()2 つのオブジェクトの内容を比較する、つまり 2 つのオブジェクトが論理的に等しいかどうかを判断するために使用されます。
  2. 行動範囲:

    • ==タイプに関係なく、任意の 2 つのオブジェクトを比較するために使用できます。
    • equals()通常、オブジェクトの内容をクラスの定義と比較するには、クラス内でメソッドをオーバーライドする必要があります。したがって、equals()動作はクラスの実装に応じて異なる場合があります。
  3. デフォルトの動作:

    • デフォルトでは、 2 つのオブジェクトの内容が同じであっても、オブジェクトへの参照が比較され、==それらが異なるインスタンスであれば==返されますfalse
    • デフォルトでは、クラスequals()から継承されたメソッドは内容ではなくオブジェクト参照を比較します。Objectしたがって、クラス内でオーバーライドされない場合、equals()メソッドは==と同じように動作します。

    equals()Object の等しいソース コードから、デフォルトではコンテンツではなくオブジェクトの参照が比較されることがわかります。

  4. カスタム比較ロジック:

    • クラスのメソッドをオーバーライドすることでequals()、オブジェクト間の比較ロジックをカスタマイズできます。これは、参照だけに依存するのではなく、オブジェクトの内容に基づいてオブジェクトが等しいかどうかを判断できることを意味します。
    • 基本的なデータ型 ( など) を比較する場合は、これらが値型で参照がないため、intこれdoubleを使用できます。==

 例:

String str1 = new String("Hello");
String str2 = new String("Hello");

System.out.println(str1 == str2);          // false,比较的是引用
System.out.println(str1.equals(str2));     // true,比较的是内容

Integer num1 = 5;
Integer num2 = 5;

System.out.println(num1 == num2);          // true,比较的是引用
System.out.println(num1.equals(num2));     // true,比较的是内容

String がアドレスではなく比較されるのはなぜですか? String クラスが equals メソッドをオーバーライドするためです。以下は String クラスのソース コードです。

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

Java ポリモーフィズムを理解するにはどうすればよいですか?

Java のポリモーフィズムはオブジェクト指向プログラミングにおける重要な概念であり、これにより、異なるクラスのオブジェクトが同じメソッド名に対して異なる応答を行うことができます。ポリモーフィズムは、オブジェクト指向プログラミングの 3 つの主要な機能の 1 つであり、他の 2 つはカプセル化と継承です。ポリモーフィズムを使用すると、共通のインターフェイスまたは親クラス参照を通じてさまざまなサブクラス オブジェクトを処理できるため、コードの再利用とインターフェイスの統一が実現します。

以下は Java ポリモーフィズムの理解と説明です。

  1. ポリモーフィズムの概念:ポリモーフィズムとは、同じメソッドまたは操作が異なるオブジェクト上で異なる表現を持つことを意味します。具体的には、ポリモーフィズムにより、異なるクラスが同じメソッド名を実装できますが、オブジェクトのタイプに基づいて異なるクラスの特定のメソッドを呼び出すことができます。この機能により、コードの柔軟性、拡張性、保守性が向上します。

  2. ポリモーフィズムの実装方法:ポリモーフィズムは主にメソッドのオーバーライド (Override) とメソッドのオーバーロード (Overload) によって実現されます。

    • メソッドのオーバーライド (オーバーライド):サブクラスは親クラスのメソッドをオーバーライドし、独自の実装を提供できます。親クラスの参照がサブクラスのオブジェクトを指している場合、同じメソッド名を呼び出すとサブクラスのメソッドが実行されます。
    • メソッドのオーバーロード (オーバーロード):同じクラス内で、同じ名前でパラメーター リストが異なる複数のメソッドを定義できます。コンパイラは、メソッド パラメータのタイプと数に基づいて、呼び出す適切なメソッドを選択します。
  3. ポリモーフィズムの適用:ポリモーフィズムにより、コードがより柔軟になり、保守しやすくなります。次のようなシナリオでよく使用されます。

    • メソッドの統合インターフェイス:ポリモーフィズムにより、異なるクラスが同じインターフェイスまたは抽象クラスを実装し、一貫した方法でこれらのメソッドを呼び出すことができます。
    • コードの再利用:ポリモーフィズムにより、共通の親クラス参照を使用してさまざまな子クラス オブジェクトを処理できるため、コードの再利用が促進されます。
    • 実行時バインディング:ポリモーフィズムにより、オブジェクトの実際の型を実行時に決定し、対応するメソッドを呼び出すことができます。これは、ランタイム ポリモーフィズムまたは動的バインディングと呼ばれます。

スレッドを作成するにはどのような方法がありますか?

Java では、スレッドを作成するさまざまな方法があります。スレッドを作成する一般的な方法は次のとおりです。

1. 継承Threadクラス:

  • Threadクラスを継承するサブクラスを作成します。
  • run()スレッドの実行ロジックが定義されているメソッドをオーバーライドします。
  • サブクラスのインスタンスを作成し、start()メソッドを呼び出してスレッドを開始します。
class MyThread extends Thread {
    public void run() {
        // 线程的执行逻辑
    }
}

MyThread myThread = new MyThread();
myThread.start();

2.Runnableインターフェースを実装します。

  • Runnableインターフェースを実装するクラスを作成します。
  • run()スレッドの実行ロジックを定義する実装メソッド。
  • 実装クラスのインスタンスを作成し、それをThreadクラスのコンストラクター メソッドにパラメーターとして渡します。
  • メソッドを呼び出してstart()スレッドを開始します。
class MyRunnable implements Runnable {
    public void run() {
        // 线程的执行逻辑
    }
}

MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();

3. 匿名の内部クラスを使用します。

  • スレッドは匿名の内部クラスを通じて作成できます。
  • 匿名の内部クラスをThreadクラスのコンストラクターに直接渡します。Runnable
Thread thread = new Thread(new Runnable() {
    public void run() {
        // 线程的执行逻辑
    }
});
thread.start();

4.Executorフレームを使用します。

  • java.util.concurrent.Executorインターフェイスとその実装クラスを使用して、スレッド プールを作成および管理します。
  • Runnableタスクをスレッド プールに送信して実行できます。
Executor executor = Executors.newFixedThreadPool(2);
executor.execute(new Runnable() {
    public void run() {
        // 线程的执行逻辑
    }
});

ダーティ リード、非再現性、ファントム リードとは何ですか?

詳細については、私のブログを参照してください: MySQL トランザクション分離レベル_Humble Jingnan Mango のブログ - CSDN ブログ

Java のガベージ コレクション メカニズム

詳細については、私のブログを参照してください: Java 仮想マシン (JVM) の詳細な説明: 実行プロセス、メモリ管理、ガベージ コレクション メカニズム_Humble Jingnan Mango のブログ - CSDN ブログ

TCP ではハンドシェイクが 2 回ではなく 3 回必要なのはなぜですか?

TCP (伝送制御プロトコル) は、双方向ハンドシェイクの代わりに 3 ウェイ ハンドシェイクを使用して信頼性の高い接続を確立します。これは主に、双方が正常に通信し、初期シーケンス番号を同期できることを保証するためです。3 ウェイ ハンドシェイクが必要な理由は次のとおりです。

  1. 双方の準備が整っていることを確認する:データ転送を行う前に、クライアントとサーバーの両方が接続を確立する準備ができていることを確認する必要があります。ハンドシェイクが 2 回しかない場合、クライアントが接続リクエストを送信しても、何らかの理由でリクエストが時間内にサーバーに到着せず、クライアントは接続が確立されたと誤って認識する状況が発生します。その後サーバーがデータを送信しても、クライアントは接続が正常に確立されたかどうかがわからないため、データを認識できません。3 ウェイ ハンドシェイクにより、接続が確立される前に双方の準備が整っていることが保証されます。

  2. 古い接続に関する問題の防止:ハンドシェイクが 2 回しかない場合、古い接続が閉じられたときにパケットがサーバーに到着するまでにネットワーク遅延が発生する可能性があります。サーバーはこれらのパケットを新しい接続と誤って認識し、問題を引き起こす可能性があります。3 ウェイ ハンドシェイクを使用すると、サーバーは古い接続が完全に閉じられたことを確認し、この混乱を回避できます。

  3. 重複接続の問題の防止: 2 回のハンドシェイクにより、特定の状況下で閉じられた接続が再確立される可能性があり、これによりリソースが不必要に浪費される可能性があります。3 ウェイ ハンドシェイクを使用すると、このような事態が発生する可能性が低くなります。

スリーウェイ ハンドシェイクの基本的なプロセスは次のとおりです。

  1. クライアントは接続要求 (SYN) をサーバーに送信します。
  2. サーバーはリクエストを受信し、クライアントの接続リクエスト (ACK) を確認します。
  3. クライアントはサーバーから確認を受信した後、再度確認応答 (ACK) をサーバーに送信します。

このプロセスにより、相手の準備が整っていること、およびシーケンス番号が同期されてデータ転送を開始できることが双方にわかります。ハンドシェイクが 2 回しかない場合、この双方向の確認効果を達成することができず、接続が不安定になったり、信頼性が低くなったりする可能性があります。したがって、TCP は 3 ウェイ ハンドシェイクを使用して、接続の信頼性と正確性を確保します。

ハッシュマップの拡張率が 0.75 である理由

HashMap の拡張係数が通常 0.75 に設定される理由は、メモリ使用量とパフォーマンスのバランスをとるための適切なトレードオフ ポイントを見つけるためです。この拡張係数は、HashMap 内の要素の数が配列の容量に近づきすぎないように HashMap を拡張するタイミングを決定し、それによって良好なパフォーマンスを維持します。

拡張係数の設定が 0.5 など小さすぎる場合、容量の上限に簡単に到達するため、HashMap はより頻繁に拡張され、追加のメモリ オーバーヘッドとパフォーマンスの損失が発生します。一方、拡張係数の設定が 1.0 など大きすぎる場合、容量が飽和に近づいたときにのみ HashMap が拡張されるため、ハッシュの競合が増加し、パフォーマンスが低下する可能性があります。

この値 0.75 は、メモリとパフォーマンスのバランスをとるための妥協点です。HashMapの負荷率を高くしすぎずに展開頻度をある程度軽減することで、良好なパフォーマンスとメモリ使用効率を維持します。この値は経験的に選択されており、通常、実際のアプリケーションでは良好に機能します。HashMap 内の要素の数が容量の 75% に達すると、HashMap はより良いパフォーマンスを維持するために自動的に拡張されます。

hashmap1.7と1.8の展開機構の違い

Java の HashMap では、特に拡張メカニズムにおいて、バージョン 1.7 と 1.8 の間にいくつかの違いがあります。2 つのバージョンの主な違いは次のとおりです。

HashMap 1.7 拡張メカニズム:

  1. 拡張トリガー条件: HashMap 1.7 では、容量 (アレイ サイズ) と負荷係数 (デフォルトは 0.75) に基づいて計算されるしきい値に達すると、拡張がトリガーされます。要素の数が負荷係数を掛けた容量に達すると、HashMap が拡張されます。

  2. 拡張方法:バージョン 1.7 では、拡張は新しい配列を作成し、元のキーと値のペアを新しい配列に再配布することによって実現されます。ハッシュ値を再計算し、キーと値のペアを新しい場所に移動する必要があるため、このプロセスには時間がかかることがあります。

  3. 拡張のタイミング:バージョン 1.7 では、HashMap の初期化時に固定サイズの配列スペースを割り当てるのではなく、put 操作中に拡張がトリガーされます。

HashMap 1.8 の拡張メカニズム:

  1. 拡張トリガー条件: HashMap 1.8 では、treeify と呼ばれる新しい拡張メカニズムが導入されています。バケット内のリンク リストの長さが特定のしきい値 (デフォルトは 8) に達すると、リンク リストは赤黒ツリーに変換され、競合が多い状況でのクエリのパフォーマンスを向上させることができます。要素の数が負荷係数を掛けた容量に達すると、拡張がトリガーされます。

  2. 展開方法: 1.7 とは異なり、バージョン 1.8 の展開方法はより効率的です。拡張する場合、単にキーと値のペアを新しい配列に再配布するのではなく、より大きな配列が元の配列上に作成され、元のバケット (リンク リストまたはツリーを含む) が新しい配列の異なる場所に配布されます。これにより、ハッシュの再計算のオーバーヘッドが軽減され、パフォーマンスが向上します。

  3. 拡張タイミング: 1.7 とは異なり、バージョン 1.8 では、Put 操作中に拡張をトリガーするのではなく、HashMap の初期化時に固定サイズの配列スペースが割り当てられます。これにより、容量拡張の頻度を減らすことができます。

要約すると、HashMap 1.8 では、パフォーマンスを向上させ、メモリ使用量を削減するために、ツリー メカニズムとより効率的な拡張方法が導入されています。これらの改善により、高負荷条件下での HashMap のパフォーマンスがより安定します。

同時ハッシュマップの導入

ConcurrentHashMap は、Java コレクション フレームワークのスレッドセーフなハッシュ テーブル実装です。マルチスレッドの同時操作をサポートするように設計されているため、マルチスレッド環境での使用が非常に効率的で、従来の HashMap よりも優れたパフォーマンスを提供します。

ConcurrentHashMap に関するいくつかの重要な機能と情報を次に示します。

  1. スレッド セーフ: ConcurrentHashMap はスレッド セーフであり、追加の同期手段を必要とせずに、複数のスレッドが同時に読み取りおよび変更できます。このため、マルチスレッド アプリケーション、特に同時実行性の高い環境での使用に最適です。

  2. セグメント ロックの設計: ConcurrentHashMap の実装ではセグメント ロック (Segment) が使用されており、内部データ構造は複数のセグメント (Segment) に分割され、各セグメントには独自のロックがあります。この設計により、複数のスレッドが異なるセグメントで動作できるようになり、ロック競合のレベルが軽減され、同時実行パフォーマンスが向上します。

  3. 高いパフォーマンス: ConcurrentHashMap は、同時実行性の高いシナリオで優れたパフォーマンスを発揮し、従来の同期ハッシュ テーブルよりも優れたパフォーマンスを提供できます。これにより、複数のスレッドが同時に読み取り操作を実行できるだけでなく、ある程度の同時書き込み操作も実行できるようになり、ロックの競合が軽減されます。

  4. スケーラビリティ: ConcurrentHashMap のセグメント化されたロック設計により、ロック競合によるパフォーマンスの低下なしに、マルチコア プロセッサや大規模なマルチスレッド アプリケーションに適切に拡張できます。

  5. 空のキー値は許可されません: HashMap とは異なり、ConcurrentHashMap では、キーまたは値を空 (null) に保存することはできません。空のキーは、キーが存在しない場合とキーが存在する場合を区別できなくなるためです。 null 値にマップされます。

  6. トラバーサル パフォーマンス: ConcurrentHashMap は、キー セット、値セット、キーと値のペアのトラバーサルなど、効率的なトラバーサル操作を提供します。これらの操作は反復中にブロックされないため、同時読み取りが可能です。

一般に、ConcurrentHashMap は強力なマルチスレッド同時コレクションであり、高パフォーマンスでスレッドセーフなハッシュ テーブル操作を必要とするシナリオに適しています。Java 同時プログラミングでは、開発者が複雑な同時アクセスの問題に対処するのに役立つ重要なツールです。ただし、ConcurrentHashMap は効率的な同時実行サポートを提供しますが、特定のシナリオでは、実際のニーズに基づいて適切なコレクション タイプを選択する必要があることに注意してください。

次号ではさらにインタビューの質問をまとめていきますので、ぜひ応援してください!

おすすめ

転載: blog.csdn.net/m0_62468521/article/details/132977391