ArrayList または LinkedList をいつ使用する必要がありますか?
ArrayList と LinkedList は、Java コレクションにオブジェクトを格納するために使用されます。フレームワーク リストを参照する 2 つのクラス。 ArrayList と LinkedList はどちらも List インターフェイスを実装します。まず、リストについて簡単に理解しましょう。
リスト (リスト) は、順序付けられた要素のコレクションであり、シーケンスとも呼ばれます。これは、リスト内の特定のインデックス位置にある要素にすばやくアクセス、追加、削除するのに役立つ要素位置ベースの操作を提供します。 List インターフェイスは、Collection と Iterable を親インターフェイスとして実装します。重複値や null 値を保存でき、インデックスを介した要素へのアクセスもサポートされます。
この記事を読んだ後に明確にするべき質問: ArrayList と LinkedList の違いは何ですか?どのような場合に ArrayList を使用し、どのような場合に LinkedList を使用する必要がありますか?
(推奨ビデオ: java ビデオ チュートリアル)
以下を追加しますArrayList と LinkedList の違いを比較するために要素を削除する例を取り上げます。
リストの末尾に要素を追加します:
要素をリストの末尾に追加するコードArrayList のキューは次のとおりです。
public boolean add(E e){ ensureCapacity(size+1);//确保内部数组有足够的空间 elementData[size++]=e;//将元素加入到数组的末尾,完成添加 return true; }
ArrayList の add() メソッドのパフォーマンスは、ensureCapacity() メソッドによって決まります。 ensureCapacity() の実装は次のとおりです。
public vod ensureCapacity(int minCapacity){ modCount++; int oldCapacity=elementData.length; if(minCapacity>oldCapacity){ //如果数组容量不足,进行扩容 Object[] oldData=elementData; int newCapacity=(oldCapacity*3)/2+1; //扩容到原始容量的1.5倍 if(newCapacitty<minCapacity) //如果新容量小于最小需要的容量,则使用最小 //需要的容量大小 newCapacity=minCapacity ; //进行扩容的数组复制 elementData=Arrays.copyof(elementData,newCapacity); } }
ArrayList の現在の容量が十分に大きい限り、add() 操作は非常に効率的であることがわかります。拡張は、ArrayList の容量要件が現在の配列サイズを超える場合にのみ必要です。拡張プロセス中に、多数の配列コピー操作が実行されます。配列がコピーされると、最終的に System.arraycopy() メソッドが呼び出されるため、add() 操作の効率は依然として非常に高くなります。
LinkedList の add() オペレーションは次のように実装されます。また、任意の要素をキューの末尾に追加します:
public boolean add(E e){ addBefore(e,header);//将元素增加到header的前面 return true; }
addBefore() メソッドが実装されます
private Entry<E> addBefore(E e,Entry<E> entry){ Entry<E> newEntry = new Entry<E>(e,entry,entry.previous); newEntry.provious.next=newEntry; newEntry.next.previous=newEntry; size++; modCount++; return newEntry; }
LinkeList はリンク リストの構造を使用しているため、容量のサイズを維持する必要がないことがわかります。この観点から見ると、ArrayList よりもパフォーマンスに一定の利点がありますが、要素を追加するたびに新しい Entry オブジェクトと追加の代入操作が必要になります。頻繁なシステムコールはパフォーマンスに一定の影響を与えます。
リスト内の任意の位置に要素を追加する
List インターフェースは、List の最後に要素を提供するだけでなく、任意の位置に要素を挿入するメソッドも提供します。 Position: void add(int index,E element);
実装が異なるため、このメソッドの ArrayList と LinkedList の間には特定のパフォーマンスの違いがあります。ArrayList は配列に基づいて実装されており、配列は連続メモリであるためです。配列内のスペース (配列内の場合) 任意の位置に要素を挿入すると、必然的にその位置以降のすべての要素が再配置されるため、効率は比較的低くなります。
次のコードは ArrayList での実装です:
public void add(int index,E element){ if(index>size||index<0) throw new IndexOutOfBoundsException( "Index:"+index+",size: "+size); ensureCapacity(size+1); System.arraycopy(elementData,index,elementData,index+1,size-index); elementData[index] = element; size++; }
各挿入操作で配列のコピーが発生することがわかります。リストの末尾に要素を追加する場合、この操作は存在しません。配列の再編成操作が多数行われると、システムのパフォーマンスが低下します。また、挿入された要素がリスト内で前にあるほど、配列の再編成のコストが大きくなります。
そして、現時点での LinkedList の利点を示します。
public void add(int index,E element){ addBefore(element,(index==size?header:entry(index))); }
LinkedList の場合、リストの最後にデータを挿入することは、任意の位置にデータを挿入することと同じであることがわかります。挿入されたデータの影響を受けませんが、挿入メソッドのパフォーマンスは前方位置により低下します。
任意の位置の要素を削除する
要素を削除する場合、List インターフェイスには任意の位置の要素を削除するメソッドが用意されています。
public E remove(int index);
ArrayList の場合、remove() メソッドと add() メソッドは同じです。任意の位置の要素を削除した後は、配列を再編成する必要があります。 ArrayList の実装は次のとおりです。
public E remove(int index){ RangeCheck(index); modCount++; E oldValue=(E) elementData[index]; int numMoved=size-index-1; if(numMoved>0) System.arraycopy(elementData,index+1,elementData,index,numMoved); elementData[--size]=null; return oldValue; }
ArrayList 内のすべての有効な要素削除操作の後に、配列を再編成する必要があることがわかります。また、削除された位置が早いほど、配列の再編成のオーバーヘッドが大きくなります。
public E remove(int index){ return remove(entry(index)); } private Entry<E> entry(int index){ if(index<0 || index>=size) throw new IndexOutBoundsException("Index:"+index+",size:"+size); Entry<E> e= header; if(index<(size>>1)){//要删除的元素位于前半段 for(int i=0;i<=index;i++) e=e.next; }else{ for(int i=size;i>index;i--) e=e.previous; } return e; }
LinkedList の実装では、最初に削除する要素をループによって見つける必要があります。削除する位置がリストの前半の場合は前から後ろへ、後半の場合は後ろから前へ検索します。したがって、前の要素を削除する場合も後の要素を削除する場合も非常に効率的ですが、リストの途中の要素を削除するには、リストのほぼ半分を走査する必要があります。効率が非常に低いです。
Capacity パラメーター
capacity パラメーターは、ArrayList や Vector などの配列ベースのリストの一意のパフォーマンス パラメーターです。初期化された配列のサイズを表します。 ArrayList に格納されている要素の数が既存のサイズを超えた場合。これは拡張され、配列の拡張により配列全体のメモリ コピーが発生します。したがって、適切な配列サイズは配列拡張の回数を減らし、システムのパフォーマンスを向上させるのに役立ちます。
public ArrayList(){ this(10); } public ArrayList (int initialCapacity){ super(); if(initialCapacity<0) throw new IllegalArgumentException("Illegal Capacity:"+initialCapacity) this.elementData=new Object[initialCapacity]; }
ArrayList は、初期配列サイズを指定できるコンストラクターを提供します:
public ArrayList(int initialCapacity)
现以构造一个拥有100万元素的List为例,当使用默认初始化大小时,其消耗的相对时间为125ms左右,当直接制定数组大小为100万时,构造相同的ArrayList仅相对耗时16ms。
遍历列表
遍历列表操作是最常用的列表操作之一,在JDK1.5之后,至少有3中常用的列表遍历方式:
● forEach操作
● 迭代器
● for循环。
String tmp; long start=System.currentTimeMills(); //ForEach for(String s:list){ tmp=s; } System.out.println("foreach spend:"+(System.currentTimeMills()-start)); start = System.currentTimeMills(); for(Iterator<String> it=list.iterator();it.hasNext();){ tmp=it.next(); } System.out.println("Iterator spend;"+(System.currentTimeMills()-start)); start=System.currentTimeMills(); int size=;list.size(); for(int i=0;i<size;i++){ tmp=list.get(i); } System.out.println("for spend;"+(System.currentTimeMills()-start));
构造一个拥有100万数据的ArrayList和等价的LinkedList,使用以上代码进行测试,测试结果:
什么情况用ArrayList or LinkedList呢?
可以看到,最简便的ForEach循环并没有很好的性能表现,综合性能不如普通的迭代器,而是用for循环通过随机访问遍历列表时,ArrayList表项很好,但是LinkedList的表现却无法让人接受,甚至没有办法等待程序的结束。这是因为对LinkedList进行随机访问时,总会进行一次列表的遍历操作。性能非常差,应避免使用。
总结
ArrayList和LinkedList在性能上各有优缺点,都有各自所适用的地方,总的说来可以描述如下:
1.对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。
对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;
而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。
2.在ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。
3.LinkedList不支持高效的随机元素访问。
4.ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间
本文来自php中文网,java教程栏目,欢迎学习!
以上がJava で ArrayList と LinkedList を使用する場合は?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。