Javaコアテクノロジーインタビューエッセンシャル(講義8)| Vector、ArrayList、LinkedListの違いは何ですか?

私たちの日常業務では、データを効率的に管理および操作できることが非常に重要です。最初に習得したC言語など、各プログラミング言語でサポートされているデータ構造が同じではないため、基本的なデータ構造を自分で実装する必要があり、管理や運用が面倒になります。対照的に、Javaの方がはるかに便利です。一般的なシナリオのニーズに対して、Javaは強力なコレクションフレームワークを提供し、開発者の生産性を大幅に向上させます。

今日お聞きしたいのは、コレクションフレームワークについてです。Vector、ArrayList、LinkedListの違いは何ですか?


典型的な答え

3つすべてが、いわゆる順序付きコレクションであるコレクションフレームワークにリストを実装します。したがって、特定の機能は比較的似ています。たとえば、場所に応じて配置、追加、または削除操作を提供し、トラバースするイテレータを提供します。その内容。ただし、特定の設計の違いにより、動作、パフォーマンス、およびスレッドセーフの点でパフォーマンスは大きく異なります。

Vectorは、Javaの初期に提供されたスレッドセーフな動的配列です。スレッドセーフが必要ない場合は、お勧めしません。結局のところ、同期には追加のオーバーヘッドがあります。Vectorは、内部でオブジェクト配列を使用してデータを格納します。容量は必要に応じて自動的に増やすことができます。配列がいっぱいになると、新しい配列が作成され、元の配列データがコピーされます。

ArrayListは、より広く使用されている動的配列の実装です。それ自体はスレッドセーフではないため、パフォーマンスが大幅に向上します。Vectorと同様に、ArrayListも必要に応じて容量を調整できますが、2つの調整ロジックは異なります。Vectorは拡張時に2倍になり、ArrayListは50%増加します。

LinkedListは、その名前が示すように、Javaによって提供される二重リンクリストであるため、上記の2つのように容量を調整する必要はなく、スレッドセーフではありません。

テストサイト分析

この質問は、Javaに連絡して以来、常に古典的なインタビューの質問であったようです。以前の回答では、3つの基本的な設計と実装について説明しました。

一般的に、さまざまなコンテナタイプに適したシナリオをいくつか追加することもできます。

  • VectorとArrayListは動的配列であり、それらの内部要素は配列の形式で順番に格納されるため、ランダムアクセスの機会に非常に適しています。末尾の要素の挿入と削除に加えて、パフォーマンスが比較的低下することがよくあります。たとえば、要素を中央の位置に挿入する場合、後続のすべての要素を移動する必要があります。
  • LinkedListはノードの挿入と削除にはるかに効率的ですが、ランダムアクセスのパフォーマンスは動的配列のパフォーマンスよりも遅くなります。

したがって、アプリケーション開発では、アプリケーションの操作が挿入、削除、またはよりランダムなアクセスに偏っているかどうかを事前に見積もることができれば、ターゲットを絞った選択を行うことができます。これは、インタビューの最も一般的な調査の角度でもあります。シナリオを前提として、適切なデータ構造を選択するため、この典型的な選択について明確にする必要があります。

Javaコレクションフレームワークを見ると、習得する必要のある多くの側面があると思います。

  • Javaコレクションフレームワークの設計構造には、少なくとも全体的な印象が必要です。
  • Javaは、メインコンテナ(コレクションとマップ)タイプを提供し、対応するデータ構造、アルゴリズムを理解または習得し、特定のテクノロジの選択について考えます。
  • 問題をパフォーマンスや並行性などの領域に拡張します。
  • 集合的フレームワークの進化と発展。

Javaの列として、可能な限りJavaを中心に拡張しようと思います。そうしないと、コレクション部分に含まれるデータ構造をリストするだけで多くのスペースが必要になります。これは、重要ではない人、データ構造とアルゴリズムが基本的なスキルであり、しばしば必要なポイントであることを意味するものではありません。一部の企業は、これらの側面を調査することで非常によく知られています(または「悪名高い」)。ここでは、例として典型的なソートアルゴリズムを習得する必要があります。少なくとも、次のことに精通している必要があります。

  • 内部ソート、少なくともマージソート、交換ソート(バブル、高速ソート)、選択ソート、挿入ソートなどの基本的なアルゴリズムを習得します。
  • 少なくともプロセスとアイデアを理解するために、大規模なデータセットを処理するためのメモリと外部ストレージの使用を習得する外部ソーティング。

アルゴリズムの調査は、実装がいかに簡単であるかだけでなく、インタビュアーは、どのソートが不安定であるか(高速ソート、ヒープソート)、または安定であるとはどういう意味かを考えるなど、収益を尋ねることがよくあります。さまざまなデータセットについて、さまざまなソートは最良または最悪の場合;特定の観点からさらに最適化する方法(ビジネスシナリオに最小限の補助スペースが必要であると仮定した場合のスペース占有など、この観点はヒープソートをマージするよりも優れています)など、単純な理解からさらに考えてみると、インタビュアーは通常、問題に対処してコミュニケーションをとるときにインタビュアーの考えを観察します。

上記は一例です。「アルゴリズム入門」、「プログラミングパール」などの関連書籍や関連チュートリアルを学ぶことをお勧めします。レコメンデーションシステムなどの特定の分野については、分野の専門家に相談することをお勧めします。純粋にインタビューの観点から、多くの友人は、インタビューのレビューと準備を支援するためにLeetCodeなどのアルゴリズムWebサイトを使用することを推奨していますが、率直に言って、私はこれらのアルゴリズムの質問をブラッシングしていません。これも慈悲深い人と賢明な人の問題です。純粋な面接官を雇わないように、面接官が得意なことを調査することを好む。

知識の拡大

まず、コレクションフレームワークの全体的な設計を理解しましょう。直感的な印象を与えるために、簡単なクラス図を作成しました。混乱を避けるために、java.util.concurrentの下にスレッドセーフコンテナを追加しませんでした。また、Mapコンテナをリストしませんでした。通常、概念的にはMapをコレクションフレームワークの一部と見なしますが、実際にはそうではありません。コレクション(コレクション)。

したがって、今日は主に狭いコレクションフレームワークに焦点を当て、残りはコラムの最後のコンテンツで説明します。

Javaのコレクションフレームワークを見ることができます。コレクションインターフェイスはすべてのコレクションのルートであり、次の3種類のコレクションを提供するように拡張されています。

  • リストは、前に紹介した中で最も順序付けられたコレクションであり、便利なアクセス、挿入、および削除操作を提供します。
  • SetとSetは、重複する要素を許可しません。これは、Listとの最も明らかな違いです。つまり、trueを返す2つのオブジェクトはありません。私たちの日々の発展の中で、要素の独自性を確保する必要がある多くの機会があります。
  • Queue / Dequeは、Javaによって提供される標準のキュー構造の実装です。コレクションの基本機能に加えて、同様の先入れ先出し(FIFO、先入れ先出し)または後入れ先出しもサポートします。 in-first-out(LIFO、Last-In)-First-Out)およびその他の特定の動作。BlockingQueueは通常、並行プログラミングの機会であるため、ここには含まれていません。したがって、並行パッケージに配置されます。 

各セットの一般的なロジックは、対応する抽象クラスに抽象化されます。たとえば、AbstractListは、さまざまなリスト操作の共通部分を集中させます。これらのコレクションは完全に分離されているわけではありません。たとえば、LinkedList自体はListとDequeの両方です。

さらにソースコードを読むと、実際には、TreeSetコードはデフォルトでTreeMapによって実際に実装されていることがわかります。Javaクラスライブラリは、値としてダミーオブジェクト「PRESENT」を作成し、挿入されたすべての要素が実際に挿入されます。キーの形式。TreeMapに;同じように、HashSetは実際にはHashMapに基づいて実装されています。これらはMapクラスのベストにすぎないことがわかります。

前述のように、さまざまな特定のコレクションを実装する必要があります。少なくとも基本的な機能と一般的な使用シナリオを理解し、Setのいくつかの実装を例として取り上げます。

  • TreeSetは自然な順次アクセスをサポートしますが、追加、削除、包含などの操作は比較的非効率的です(log(n)時間)。
  • HashSetはハッシュアルゴリズムを使用します。理想的には、ハッシュが正常であれば、一定時間の加算、削除、および包含操作を提供できますが、順序を保証するものではありません。
  • LinkedHashSetは、挿入順序でレコードの二重リンクリストを内部的に構築するため、挿入順序に従ってトラバースする機能を提供します。同時に、一定時間の追加、削除、および包含操作も保証します。これらのパフォーマンスリンクリストのオーバーヘッドを維持する必要があるため、操作はHashSetよりもわずかに低くなります。
  • 要素をトラバースする場合、HashSetのパフォーマンスはそれ自体の容量の影響を受けるため、初期化するときは、必要な場合を除いて、背後にあるHashMapの容量を大きく設定しすぎないでください。LinkedHashSetの場合、内部リンクリストによって提供される利便性により、トラバーサルパフォーマンスは要素の数にのみ関連します。

本日紹介したコレクションクラスはスレッドセーフではありません。java.util.concurrentのスレッドセーフコンテナについては、このコラムの後半で紹介します。ただし、これらのコレクションが並行プログラミングシナリオをまったくサポートできないことを意味するわけではありません。コレクションツールクラスでは、次のような一連の同期メソッドが提供されます。

static <T> List<T> synchronizedList(List<T> list)

同様の方法を使用して、基本的なスレッドセーフコレクションを実現できます。

List list = Collections.synchronizedList(new ArrayList());

その実装は基本的に、同期を通じて、get、set、addなどのすべての基本的なメソッドに基本的な同期サポートを追加することです。これは非常に単純で失礼ですが、非常に実用的でもあります。これらのメソッドによって作成されたスレッドセーフコレクションは、反復中のフェイルファスト動作に準拠していることに注意してください。予期しない同時変更が発生すると、予期しない動作を回避するために、ConcurrentModificationExceptionができるだけ早くスローされます。

よく調査されるもう1つの質問は、Javaが提供するデフォルトのソートアルゴリズム、特定のソート方法、および設計のアイデアを理解することです。

Arrays.sort()とCollections.sort()を区別する必要があるため、この質問自体はちょっとした罠です(最下層はArrays.sort()を呼び出すことです);どのデータ型;データの大きさセット(データセットが小さすぎる、複雑な並べ替えは不要、Javaはバイナリ挿入の並べ替えを直接実行します)など。

  • プリミティブデータ型の場合、現在、いわゆるデュアルピボットクイックソートが使用されています。これは改良されたクイックソートアルゴリズムです。初期バージョンは比較的従来のクイックソートです。ソースコードを読むことができます。
  • オブジェクトデータ型の場合、現在、TimSortが使用されています。これは、マージとバイナリソート(binarySort)を組み合わせた最適化されたソートアルゴリズムでもあります。TimSortはJavaの最初の作成ではありません。簡単に言えば、そのアイデアは、データセット(ここではrunと呼ばれます)でソートされたパーティションを見つけ、これらのパーティションをマージしてソートの目的を達成することです。

さらに、Java 8では、最新のマルチコアプロセッサの計算能力を最大限に活用する並列ソートアルゴリズム(parallelSortメソッドを直接使用)が導入されました。基盤となる実装は、fork-joinフレームワーク(fork-join)に基づいています。後のコラムで比較的詳細に紹介します)、処理されたデータセットが比較的小さい場合、ギャップは明らかではないか、さらに悪化しますが、データセットが数万または100万を超えると、改善はプロセッサとシステム環境によっては、非常に大きくなります。

ソートアルゴリズムはまだ改善中です。最近、2軸クイックソートの実装の作者がさらに改善を提出しました。これは長年研究されており、現在レビューと検証の段階にあります。著者のパフォーマンステストの比較によると、マージソートに基づく実装と比較して、新しい改善により、ランダムデータソートの速度が10%から20%向上し、他の特性を持つデータセットでも数倍改善されました。 。興味がある場合は、特定のコードと概要を参照できます。

http://mail.openjdk.java.net/pipermail/core-libs-dev/2018-January/051000.html

Java 8では、JavaプラットフォームがLambdaとStreamをサポートし、対応するJavaコレクションフレームワークも大幅に拡張されて、コレクションに対応するストリームまたはparallelStreamsの作成と同様のメソッドをサポートします。機能コードを非常に便利に実装できます。

Javaソースコードを読むと、これらのAPIの設計と実装は非常にユニークであることがわかります。これらは抽象クラスではなく、Collectionなどのインターフェイスのデフォルトメソッドの形式で実装されています。これは言語レベルでのJava8の新機能であり、インターフェースでデフォルトのメソッドを実装できます。理論的には、Collectionsなどのツールクラスで最初に実装したメソッドのほとんどは、対応するインターフェースに変換できます。これに応えて、オブジェクト指向のテーマでは、Java言語の基本的なオブジェクト指向メカニズムの進化を具体的に整理します。

Java 9では、Java標準クラスライブラリは、List.of()、Set.of()などの一連の静的ファクトリメソッドを提供します。これにより、小さなコンテナインスタンスを構築するためのコードの量が大幅に簡素化されます。業界の実際の経験によると、かなりの数のコレクションインスタンスの容量が非常に限られており、ライフサイクル中に変更されないことがわかりました。ただし、元のJavaクラスライブラリでは、次のように記述する必要がある場合があります。

ArrayList<String>  list = new ArrayList<>();
list.add("Hello");
list.add("World");

新しいコンテナ静的ファクトリメソッドでは、1文のコードで十分であり、不変性が保証されます。

List<String> simpleList = List.of("Hello","world");

さらに、さまざまな静的ファクトリメソッドによって作成されたインスタンスは、いわゆるベストプラクティスの一部も適用します。たとえば、不変であり、スレッドセーフの要件を満たしています。拡張を考慮する必要がないため、スペースアッパーはより多くなります。コンパクトなど。

ofメソッドのソースコードを見ると、特に興味深い場所も見つかります。Javaはすでにいわゆる変数パラメーター(varargs)をサポートしていることがわかっていますが、公式のクラスライブラリは、特定のメソッドを備えた一連のメソッドを提供しています。パラメータの長さ、それは非常にエレガントではないようです、なぜですか?これは実際には最適なパフォーマンスのためです。パフォーマンスの感度を達成する必要がある場合、JVMは可変長パラメーターを処理するときに明らかに追加のオーバーヘッドがあります(カタツムリを実行しているネチズン注:java可変長パラメーター処理は新しい配列を使用します)APIも参照できます。 

今日はVerctor、ArrayList、LinkedListから始めて、設計と実装の違い、適切なアプリケーションシナリオなどを徐々に分析し、コレクションフレームワークをさらに簡単に要約し、基本的なアルゴリズムからAPIデザインまでコレクションフレームワークのさまざまな改善点を紹介しました。実装。それがあなたの毎日の開発とAPI設計に役立つことを願っています。

1つのレッスンを練習する

今日私たちが話していることを知っていますか?最初にアプリケーションシナリオについて考えるための質問を残してください。たとえば、VIP顧客のタスクが最初に処理されることを期待して、クラウドコンピューティングタスクスケジューリングシステムを実装する必要があります。どのデータ構造または標準の収集タイプを使用できますか?さらに、どのデータ構造に基づいて最も類似したシナリオがありますか?


他の古典的な答え

以下は、各レッスンの質問に対するネチズンのレイピリの父親の回答からのものです。

このトピックでは、当然、優先キューについて考えることになりますが、vipの再分類、つまり同じレベルのvipに対する同等の権限の問題も考慮する必要があるため、優先キューの優先ルールの問題に加えて、直接およびVIPレベルに関連する優先度キュー、同じレベルの複数の顧客が1人の顧客の多数のタスクによってブロックされないという問題を考慮する必要があります。データ構造は確かに基礎です。このシナリオがこの質問では、スケジュールされるデータはおそらくredisに配置されます。

次の答えは、ネチズンのSunXiaogangからのものです。

最初のものを選んでください。読み書きの効率の問題に関しては、表現が不足している、または絶対的ではないと感じています。

1.すべての追加と削除が新しいメモリを開くわけではありません。新しいメモリがない場合は、効率が活用されます。

2.テールの削除は、新しいメモリを開く必要はありません。最後のオブジェクトを削除するだけです。

以前、私はArrayList機能も受け取りました。これは、ランダムアクセスが高速で、追加と削除の効率が低くなっています。絶対的なものではなく、ソースコードを見るまで知りませんでした。
直接的な結果として、ArrayListの使用に適したシナリオでは、この一般的なステートメントのためにLinkedListが選択されます。

以下は、ネチズンの公式アカウントからの回答です-Technology Sleeplessly:

Vector、ArrayList、およびLinkedListはすべて線形データ構造ですが、実装シナリオとアプリケーションシナリオには違いがあります。

1
下層の実装ArrayListは配列で実装され、LinkedListは二重リンクリストで実装され、Vectorは配列で実装されます。

2読み取りおよび書き込みメカニズム
ArrayList挿入された要素が現在の配列の事前定義された最大値を超える場合、配列を拡張する必要があります。拡張プロセスでは、基になるSystem.arraycopy()メソッドを呼び出して、多数の配列コピー操作を実行する必要があります。 ;要素を削除しても減少しません配列の容量(配列の容量を減らす必要がある場合は、trimToSize()メソッドを呼び出すことができます);要素を探すときは、配列をトラバースして見つける必要がありますnull以外の要素も同じように。

要素を挿入するとき、LinkedListは新しいEntryオブジェクトを作成し、対応する要素の前後で要素の参照を更新する必要があります。要素を検索するときは、リンクリストをトラバースする必要があります。要素を削除するときは、リンクをトラバースする必要があります。リストをクリックして削除する要素を見つけ、リンクリストからこの要素を削除します。
VectorとArrayListの容量拡張メカニズムは、要素を挿入する場合にのみ一貫性がありません。Vectorの場合、デフォルトでサイズ10のObject配列が作成され、capacityIncrementは0に設定されます。挿入された要素配列のサイズが十分でない場合、capacityIncrementが0より大きい場合、Object配列のサイズは次のようになります。既存のサイズ+ capacityIncrementに拡張されます。capacityIncrement<= 0の場合、オブジェクト配列のサイズは既存のサイズの2倍に拡張されます。

3読み取りと書き込みの効率

ArrayListの要素を追加および削除すると、配列のメモリ割り当てスペースが動的に変更されます。そのため、挿入と削除の速度は遅くなりますが、取得速度は速くなります。

LinkedListはリンクリストに基づいてデータを格納するため、要素の追加と削除の速度は速くなりますが、取得速度は遅くなります。

4スレッドセーフ

ArrayListとLinkedListはスレッドセーフではありません。Vectorは、同期された実装に基づくスレッドセーフなArrayListです。

注意すべき点:シングルスレッドはArrayListを使用しようとする必要があり、Vectorは同期によりパフォーマンスが低下します。マルチスレッド環境でも、Collectionsクラスで提供されているsynchronizedList(リストリスト)メソッドを使用して、スレッドセーフな同期リストオブジェクトを返します。

質問への回答

PriorityBlockingQueueまたはDisruptorを使用して、スケジューリング戦略としてタスクの優先順位に基づいて実行スケジューリングシステムを実装します。

以下は、各レッスンに対するネチズンのジョシュアの回答からのものです。

これはJavaの対象であるため、PriorityBlockingQueueを使用してください。
それが実際のシーンである場合、それは間違いなく高可用性と耐久性のあるソリューションを検討します。
実際、バンクウィンドウを参照する必要があると思います。同時に3つのウィンドウ、つまり3つのキューがあります。バンクはコンシューマスレッドです。特定のウィンドウが最初にVIPです。VIPがない場合は、それも機能します。普通のお客様。これを実現するには、ディスパッチャを用意するか、VIPチャネルの入力を禁止したままにします。そうすると、アイドル状態のときに他のキューからVIPカウンタが盗まれます。

以下は、各レッスンに対するnetizenlinco_66の回答から得られたものです。

処理されるタスクにはシーケンス関係があるため、最初に考えることは優先度付きキューを使用することです。PriorityQueueを使用して、VIPユーザーの優先度を最高に設定し、処理を優先します。オペレーティングシステムのスケジューリングアルゴリズムから学び、他のユーザーのために、さまざまな公正な優先度選択アルゴリズムを設計することもできます(キューイングシーケンスに基づいて、タスクのスケジューリングに必要な時間の長さに基づいて(オペレーティングシステムの短いジョブ優先度アルゴリズム)システム)ソート、高い応答率((使用時間+待機時間)/待機時間)ソートの優先順位)、PriorityQueueとの組み合わせ。
ほとんどの同様のシナリオは、キューベースのデータ構造に基づいています。実際のツールに関しては、メッセージキュー(MQ)は非常に簡単な例です。メッセージキューを使用して、ユーザーリクエストに対してカット操作を実行できます。フォアグラウンドは迅速に応答し、バックグラウンドはプライベート処理操作を実行します。
さらに、最適化は次のように考えることができます。分散システムの利点を使用して、処理のためのより高い計算能力を備えたサーバーにVIPユーザー要求を分散します。高可用性の特性を実現!

次の答えはネチズンジャッキズから来ています:

コレクション:それは一種のコンテナのようなものです。オブジェクトの保存、取得、操作に使用されるコンテナ。

1.
配列のデメリット①配列の長さが不変②配列には有効な要素の数を表示する方法がありません

2.
コレクションの特徴①コレクションの長さは可変です
②コレクションはあらゆる種類のオブジェクトを格納できます
③コレクションオブジェクトのみを格納でき

ます
。3 。コレクションフレームワークjava.util.Collection:コレクション階層のルートインターフェイス
    | --- java.util.List:整然とした繰り返し可能。
        | --- ArrayList:配列構造体を使用して要素を格納します。クエリ操作が何度も行われる場合に選択
        | --- LinkedList:リンクリスト構造を使用して要素を格納します。追加および削除操作では、多くの場合、
        | ---ベクトル:
    | --- java.util.Set:を選択します。無秩序で、繰り返しは許可されません。
        | --- HashSet:Setインターフェースの典型的な実装クラスです。
            要素が存在するかどうかを判断するための基礎は、最初にhashCode値を比較し、hashCodeが存在する場合は、equals()を介してコンテンツを比較します
                                     。hashCode値が存在しない場合は、直接保存します。

            注:hashCodeとequalsを書き換える必要があります。一貫性があります!
            | --- LinkedHashSet:HashSetと比較して、リンクリストのメンテナンス要素の順序が多くなっています。トラバース効率はHashSetよりも高く、追加および削除効率はHashSetよりも低くなります
        | --- TreeSet:独自の並べ替えメソッドがあります
            |-自然な並べ替え(比較可能):
                ①実装するTreeSetコレクションにオブジェクトのクラスを追加する必要がありますComparableインターフェース
                ②compareTo(Object o)メソッドを実装するには
            |-カスタムソート(Comparator)
                ①Comparatorインターフェースを実装するクラスを作成する
                ②compare(Object o1、Object o2)メソッドを
                実装する③実装クラスのインスタンスをパラメータとして渡すTreeSetコンストラクターへ

 

 

 

 

おすすめ

転載: blog.csdn.net/qq_39331713/article/details/114093929