1、Javaの一般的なコンテナ:リスト、セット、地図
リスト:
- コレクション継承されたインタフェース(パブリックインターフェイスの一覧は<E>は、コレクション<E>を拡張し)、重複する値を注文し、許可されます。
セットする:
- これは、Collectionインタフェース(パブリックインターフェイスが設定し、<E>は、コレクション<E>を拡張し)、無秩序を継承し、重複する値を許可していません。
地図:
- キーと値の貯蔵容器(パブリック・インタフェースMap <K、V>)があります。
2、コレクションとコレクションの違い
コレクション:
- コレクションクラスのコレクション共通のインタフェースである(出典:パブリックインタフェースCollection <E>は、反復処理可能<E>拡張します)。
- ソースコードを見ることで見つけることができる、それがユーザーインターフェース、彼の即時の後継インタフェースのリストとセットのいくつかの共通のセットですが含まれています。
コレクション:
- コレクションツール(出典:パブリッククラスコレクション)のコレクションです。
- これは、ソートなどの静的メソッドのコレクションで一連の動作を提供していますので、上の逆()と:、synchronizedCollection()リバース:ソート()、セキュリティのコレクション。
3、のArrayListとLinkedListの差
配列リスト:
- データ構造の基礎となること配列、より効率的なクエリであり、ゆっくりと添加削除する(指定された場所の効率で元素の添加の終わりに、デフォルトでは、背中のシフトされ、以下の要素の位置を指定する必要があるため、比較的低いです)。
LinkedListの:
- 基礎となるデータ構造は、追加高速削除、クエリ効率が比較的遅い二重連結リスト(旧ノードに前のポイント、ノードの後の次の点)です。
4、ArrayListのとベクトルとの差分
配列リスト:
- 非スレッドセーフ、高い読み取り率。
ベクター:
- (同期ソースコードを使用して、クラスの表示方法)、低い読み取り効率(推奨CopyOnWriteArrayListと、ほとんどのシーンを読み書きするためのクラス)は、スレッドセーフ。
5、ハッシュマップとハッシュテーブルとの間の差
HashMapの:
- 非スレッドセーフが、効率が比較的高い、空のキー値を可能にする(基礎となるデータ構造は、リンクされたリスト+ +赤黒木(jdk8)+リスト又はアレイ(JDK7)を使用して配列です)。
ハッシュ表:
- スレッドの安全性は、空のキーを許可しない、効率が比較的低い(のConcurrentHashMapを推奨します)。
6、ハッシュマップとTreeMapの使用シナリオ
HashMapの:
- 一般に、挿入、削除、要素の位置決めは、HashMapの(共通)を使用します。
TreeMapの:
- あなたは、推奨のTreeMapの順序集合が必要な場合。
7、HashMapの実装原理
一例として、操作を配置するには:
- 最初は、キーハッシュコードのハッシュ値に基づくであろう(セクションソース:?復路(キー==ヌル)0:(H = key.hashCode())^(H >>> 16))、配列の要素に価値ハッシュに従って位置(添字)、要素の位置がこの位置に直接エレメント次いで、存在しない場合は、そうでない場合、同一の要素かどうかを決定し、もしそうであれば、カバー、又は方法に参加するために、リンクされたリストを作成する(ファスナーを使用して競合を解決します)鎖の末端に、頭の上にチェーンを添加した後、場合以上8、赤黒木ストレージの使用。
- 重要な要素は、の要素を含めることだけではなく、値だけで入れてください。
8、HashSetの実装原理
一例として、操作を追加するには:
- (ヌル== PRESENTリターンmap.put(E))にソースを追加し、基礎となるマップが実装されていることがわかる、統一を使用して唯一のキーマップとして渡された値、およびマップ値PRESENT。
9、イテレータ:イテレータ
イテレータ:
- 軽量オブジェクト(作成された小さな価格)は、主にコレクション除去作業をトラバースするために使用されます。
- 次のサンプルコード
package com.spring.test.service.demo; import java.util.*; /** * @Author: philosopherZB * @Date: 2019/10/1 */ public class Demo { public static void main(String[] args){ List<String> list = new ArrayList<>(5); for(int i=0;i<5;i++){ list.add("Test" + i); System.out.println("输入:Test" + i); } //利用iterator()返回一个Iterator对象 Iterator<String> it = list.iterator(); //判断是否还存在元素 while(it.hasNext()){ //第一次调用next()会返回集合中的第一个元素,之后返回下一个 String s = it.next(); if("Test3".equals(s)) //移除某个元素 it.remove(); } list.forEach(l->{ System.out.println(l); }); } }
10、ArrayList 扩容源码解析(JDK8)
源码解析:
- 首先我们使用 ArrayList<String> list = new ArrayList<>(5)创建一个ArrayLsit,这表明创建的ArrayList初始容量为5.
- 源码如下:
//默认初始容量10 private static final int DEFAULT_CAPACITY = 10; //一个空的默认对象数组,当ArrayList(int initialCapacity),ArrayList(Collection<? extends E> c)中的容量等于0的时候使用 private static final Object[] EMPTY_ELEMENTDATA = {}; //一个空的默认对象数组,用于ArrayList()构造器 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //一个对象数组,transient表示不能序列化 transient Object[] elementData; //数组大小 private int size; //以传入的容量构造一个空的list public ArrayList(int initialCapacity) { //如果传入值大于0,则创建一个该容量大小的数组。 if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { //否则如果传入值等于0,则创建默认空数组 this.elementData = EMPTY_ELEMENTDATA; } else { //如果小于0则抛出异常 throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
- 接着我们使用add方法添加一个字符串到该list中,list.add("Test")。进入add源码会发现,真正的扩容是发生在add操作之前的。
- 源码如下:
//默认添加在数组末尾 public boolean add(E e) { //添加之前先确认是否需要扩容 ensureCapacityInternal(size + 1); // Increments modCount!! //新加入的元素是添加在了数组的末尾,随后数组size自增。 elementData[size++] = e; return true; }
- 进入ensureCapacityInternal()方法查看对应源码如下:
private void ensureCapacityInternal(int minCapacity) { //先通过calculateCapacity方法计算最终容量,以确认实际容量 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
- 到这一步,我们需要先进入calculateCapacity()方法看看他是如何计算最后容量的,源码如下:
private static int calculateCapacity(Object[] elementData, int minCapacity) { //如果elementData为默认空数组,则比较传入值与默认值(10),返回两者中的较大值 //elementData为默认空数组指的是通过ArrayList()这个构造器创建的ArrayList对象 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } //返回传入值 return minCapacity; }
- 现在我们确认了最终容量,那么进入ensureExplicitCapacity,查看源码如下:
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code //如果最终确认容量大于数组容量,则进行grow()扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); }
- 可以看到,只有当最终容量大于数组容量时才会进行扩容。那么以我们上面的例子而言具体分析如下:
- 首先因为我们创建的时候就赋了初始容量5,所以elementData.length = 5。
- 当我们add第一个元素的时候,minCapacity是等于size + 1 = 1的。
- 此时minCapacity - elementData.length > 0条件不成立,所以不会进入grow(minCapacity)方法进行扩容。
- 以此类推,只有添加到第五个元素的时候,minCapacity = 6 大于 elementData.length = 5,这时就会进入grow(minCapacity)方法进行扩容。
- grow()以及hugeCapacity()源码如下:
//可分配的最大数组大小 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //扩容 private void grow(int minCapacity) { // overflow-conscious code //oldCapacity表示旧容量 int oldCapacity = elementData.length; //newCapacity表示新容量,计算规则为旧容量+旧容量的0.5,即旧容量的1.5倍。如果超过int的最大值会返回一个负数。 //oldCapacity >> 1表示右移一位,对应除以2的1次方。 int newCapacity = oldCapacity + (oldCapacity >> 1); //如果新容量小于最小容量,则将最小容量赋值给新容量(有时手动扩容可能也会返回<0,对应方法为ensureCapacity()) if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //如果新容量大于MAX_ARRAY_SIZE,则执行hugeCapacity(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); } private static int hugeCapacity(int minCapacity) { //如果最小容量超过了int的最大值,minCapacity会是一个负数,此时抛出内存溢出错误 if (minCapacity < 0) // overflow throw new OutOfMemoryError(); //比较最小容量是否大于MAX_ARRAY_SIZE,如果是则返回Integer.MAX_VALUE,否则返回MAX_ARRAY_SIZE return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
(以上所有内容皆为个人笔记,如有错误之处还望指正。)