Arraylistの利点(高速なクエリ、遅い追加と削除)
ArrayListの欠点(追加と削除が遅い、クエリが速い)
実際、これら2つのポイントを理解することは比較的簡単です。つまり、データ構造(配列の理解)を理解することです。各データ構造には独自の時間と空間の複雑さがあり、効率の問題はO(1)に似ています。 O(n)、O(logn)これらは複雑であり、Java実装のレベルもあります。この記事では、これらのレベルの効率とパフォーマンスの問題について説明します
最初の部分:配列(データ構造)
それは本データ構造で言及されています:配列は拡張線形データ構造と見なすことができ、その特殊性はスタックやキューなどのデータ要素の操作には反映されませんが、データ要素の構成には反映されますオン。線形テーブルを構成する要素の観点から見ると、配列にはデータ要素の特定の構造があり、配列は線形テーブルの拡張です。
自宅に近い:
配列は、私たちがよく知っているデータ構造であり、高水準言語は一般にこのデータ型をサポートしています。
配列は線形テーブルです。つまり、リンクされたリスト、キュー、スタックなどの配列がすべて線形構造であることを除いて、データラインの構造は直線に似ています。
非線形テーブルは、二分木、スタック、図面等であるデータとの間の単純な関係ていません
配列とリンクリストの関係は?
通常、私たちは答えます:リンクリストは挿入と削除に適しており、時間の複雑さはO(1)です。配列は検索に適しており、検索時間の複雑さはO(1)です。
実際、この式は不正確です。配列は検索に適していますが、検索の時間の複雑さはO(1)ではありません。したがって、正しい式は、次の表(インデックス位置)のランダムアクセスに従って、配列がランダムアクセスをサポートしていることです。時間の複雑さはO(1)です
特定のデータ構造配列の知識については、特定の本を読んでそれについて学ぶことができます。コンピュータ内の配列の格納方法と計算方法はすべて研究および学習できます。著者を深く理解するために、この方法を選択する理由そして、デザインは非常に役立ち、またなぜ私たちが求めているのかを理解するのに役立ちますか?
パート2:ArrayListのソースレベルからの分析
配列についてはすでにいくつかの知識を述べました。ここで、配列についてある程度理解する必要があります。実際、あまり深く理解しなくても問題ありません。配列の固定された特性を知っていれば十分です。したがって、arraylistの最下層は配列に基づいています。では、arraylistの追加と削除が速く、クエリが遅い理由を理解する必要があります。これをソースコードの観点から分析しましょう。
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
//入参为索引值,也就是下标
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
// Positional Access Operations
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
上記のコードはarraylistのソースコード実装です。これは、最下層が配列に基づいて実装され、配列の特性もクエリの追加と削除が迅速であるためです。データは添え字に基づいて直接取得されるため、時間の複雑さはO(1)であり、arraylistです。 RandomAccessインターフェースが実装され、ランダムアクセスがサポートされているため、クエリの効率が非常に速いことがわかります。
遅い追加と削除の問題は徹底的に調査することができ、言うべきことは多くありません:ソースコードを見ることが配列データ構造を理解するのにさらに役立つかもしれません
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
@SuppressWarnings("unchecked")
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
上記は、配列の最後に要素を追加したデータです。時間の複雑さは影響しません。これは、拡張操作(これは、拡張プロセス中にパフォーマンスを消費する操作でもあります)のみを検証します。画像を使用して、鮮明に説明してください:
ただし、指定した場所に追加すると、パフォーマンスに大きく影響します。
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
//在这一步执行native声明的方法的时候,就是耗费时间和性能的一步操作了,在底层具体的实现方式是这样的
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
次に、パフォーマンスが比較的低い理由は、位置2に要素を追加するなど、要素を追加するたびに、位置1の後の要素を後方に移動する必要があるためです。移動のプロセスは、上記のコードであるパフォーマンスに影響しますarrayCopy操作のこのステップでは、拡張の検証も行われます。
メソッドを削除します。
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
上記のソースコードを見ると、非常に明確であるはずです。これは、実際にはaddメソッドと同じです。動的画像の説明を次に示します。
これで、arrayListクエリの追加と削除が遅いことをよく理解できたはずです。説明に問題がある場合は、一緒に話しましょう。この記事の配列の説明は特に詳細ではないかもしれませんが、理解できない場合は自分で学ぶことができます。将来、いくつかの基本的なデータ構造を追加します。