目次
2. Collection インターフェースの一般的なメソッド:
2. java.utils パッケージで定義されたイテレータ インターフェイス: Iterator
3. JDK 5.0 の新機能 - for ループの強化: (foreach ループ)
4. コレクションサブインターフェース: リストインターフェース
5. Collection サブインターフェース: Set インターフェースの概要
2. 要素の追加プロセス: (HashSet を例にします)
1. セットと配列
1. コレクションおよびアレイ ストレージ データの概要:
コレクションと配列は、Java コンテナと呼ばれる、複数のデータを格納および操作する構造です。説明: このときのストレージとは主にメモリ レベルのストレージを指し、永続的なストレージ (データベース内の .txt、.jpg、.avi) は含まれません。
2. アレイストレージの特徴:
初期化されると、その長さが決定されます。配列が定義されると、その要素の型が決定されます。指定されたタイプのデータのみを操作できます。
例: String[] arr
、、、int[] arr1
Object[] arr2
3. アレイストレージの欠点:
- 初期化すると、その長さを変更することはできません。
- 配列で提供されるメソッドは非常に限られており、データの追加、削除、挿入などの操作には非常に不便で非効率的です。
- 配列内の実際の要素数を取得する必要があるため、配列には利用可能な既製のプロパティやメソッドがありません。
- 配列ストレージ データの特徴: 整然としており、反復可能です。無秩序で再現性のない要件の場合、満たすことはできません。
4. 集合ストレージの利点:
配列ストレージ データの欠点を解決します。
5. コレクションの分類
Java コレクションは、コレクションとマップの 2 つのシステムに分けることができます。
- コレクション インターフェイス: オブジェクトのセットにアクセスするためのメソッドのコレクションを定義する単一のデータ列
- リスト: 順序付けられた反復可能な要素のコレクション
- セット: 順序付けされておらず、反復不可能な要素のセット
- マップ インターフェイス: 2 列データ。マッピング関係を持つ「キーと値のペア」のコレクションを保存します。
6. コレクションの骨格構造
|----Collection接口:单列集合,用来存储一个一个的对象
|----List接口:存储有序的、可重复的数据。 -->“动态”数组
|----ArrayList:作为List接口的主要实现类,线程不安全的,效率高;底层采用Object[] elementData数组存储
|----LinkedList:对于频繁的插入删除操作,使用此类效率比ArrayList效率高底层采用双向链表存储
|----Vector:作为List的古老实现类,线程安全的,效率低;底层采用Object[]数组存储
|----Set接口:存储无序的、不可重复的数据 -->数学概念上的“集合”
|----HashSet:作为Set接口主要实现类;线程不安全;可以存null值
|----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加顺序遍历;对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
|----TreeSet:可以按照添加对象的指定属性,进行排序。
|----Map:双列数据,存储key-value对的数据 ---类似于高中的函数:y = f(x)
|----HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
|----LinkedHashMap:保证在遍历map元素时,可以照添加的顺序实现遍历。
原因:在原的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
对于频繁的遍历操作,此类执行效率高于HashMap。
|----TreeMap:保证照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
底层使用红黑树
|----Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
|----Properties:常用来处理配置文件。key和value都是String类型
复制代码
2. 収集インターフェース
- Collection インターフェイスは、List、Set、および Queue インターフェイスの親インターフェイスです。このインターフェイスで定義されたメソッドは、Set コレクションと List および Queue コレクションの両方を操作するために使用できます。
- JDK は、このインターフェイスの直接実装を提供しませんが、より具体的なサブインターフェイス (Set や List など) の実装を提供します。
- JDK 5.0 より前では、Java コレクションはコンテナ内のすべてのオブジェクトのデータ型を失い、すべてのオブジェクトをオブジェクト型として扱いました。JDK 5.0 ではジェネリックスが追加されたため、Java コレクションはコンテナ内のオブジェクトのデータ型を記憶できるようになりました。
1. 単一カラムのコレクションフレーム構造
|----Collection接口:单列集合,用来存储一个一个的对象
|----List接口:存储有序的、可重复的数据。 -->“动态”数组
|----ArrayList:作为List接口的主要实现类,线程不安全的,效率高;底层采用Object[] elementData数组存储
|----LinkedList:对于频繁的插入删除操作,使用此类效率比ArrayList效率高底层采用双向链表存储
|----Vector:作为List的古老实现类,线程安全的,效率低;底层采用Object[]数组存储
|----Set接口:存储无序的、不可重复的数据 -->数学概念上的“集合”
|----HashSet:作为Set接口主要实现类;线程不安全;可以存null值
|----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加顺序遍历;对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
|----TreeSet:可以按照添加对象的指定属性,进行排序。
复制代码
グラフィック:
2. Collection インターフェースの一般的なメソッド:
- に追加
add(Object obj)
addAll(Collection coll)
- 有効な要素の数を取得する
int size()
- 空のコレクション
void clear()
- 空のコレクションですか
boolean isEmpty()
- 要素が含まれていますか
boolean contains(Object obj)
: 要素のequalsメソッドを通じて同一オブジェクトかどうかを判定しますboolean containsAll(Collection c)
:要素のequalsメソッドを呼び出しても比較されます。2 つのコレクションの要素を 1 つずつ比較します
- 消去
boolean remove(Object obj)
:要素のequalsメソッドで削除対象の要素であるか判定します。最初に見つかった要素のみが削除されますboolean removeAll(Collection coll)
: 現在のセットの差を取る
- 2 つの集合の交点を求めます
boolean retainAll(Collection c)
: c に影響を与えずに、交差の結果を現在のコレクションに保存します。
- セットは等しいですか
boolean equals(Object obj)
- オブジェクトの配列に変換する
Object [] toArray()
- コレクションオブジェクトのハッシュ値を取得する
- `hashCode()`
复制代码
- トラバース
- `iterator()`:返回迭代器对象,用于集合遍历
复制代码
コード例:
@Test
public void test1() {
Collection collection = new ArrayList();
//1.add(Object e):将元素添加到集合中
collection.add("ZZ");
collection.add("AA");
collection.add("BB");
collection.add(123);
collection.add(new Date());
//2.size():获取添加元素的个数
System.out.println(collection.size());//5
//3.addAll(Collection coll1):将coll1集合中的元素添加到当前集合中
Collection collection1 = new ArrayList();
collection1.add("CC");
collection1.add(213);
collection.addAll(collection1);
System.out.println(collection.size());//9
//调用collection1中的toString()方法输出
System.out.println(collection);//[ZZ, AA, BB, 123, Tue Apr 28 09:22:34 CST 2020, 213, 213]
//4.clear():清空集合元素
collection1.clear();
System.out.println(collection1.size());//0
System.out.println(collection1);//[]
//5.isEmpty():判断当前集合是否为空
System.out.println(collection1.isEmpty());//true
}
@Test
public void test2() {
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Tom", 23));
coll.add(new Person("Jarry", 34));
coll.add(false);
//6.contains(Object obj):判断当前集合中是否包含obj
//判断时需要调用obj对象所在类的equals()方法
System.out.println(coll.contains(123));//true
System.out.println(coll.contains(new Person("Tom", 23)));//true
System.out.println(coll.contains(new Person("Jarry", 23)));//false
//7.containsAll(Collection coll1):判断形参coll1中的元素是否都存在当前集合中
Collection coll1 = Arrays.asList(123, 4566);
System.out.println(coll.containsAll(coll1));//flase
//8.remove(Object obj):从当前集合中移除obj元素
coll.remove(123);
System.out.println(coll);//[456, Person{name='Tom', age=23}, Person{name='Jarry', age=34}, false]
//9.removeAll(Collection coll1):差集:从当前集合中和coll1中所有的元素
Collection coll2 = Arrays.asList(123, 456, false);
coll.removeAll(coll2);
System.out.println(coll);//[Person{name='Tom', age=23}, Person{name='Jarry', age=34}]
}
@Test
public void test3() {
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Tom", 23));
coll.add(new Person("Jarry", 34));
coll.add(false);
//10.retainAll(Collection coll1):交集:获取当前集合和coll1集合的交集,并返回给当前集合
Collection coll1 = Arrays.asList(123, 345, 456);
boolean b = coll.retainAll(coll1);
System.out.println(b);//true
System.out.println(coll);//[123, 456]
//11.equals(Object obj):返回true需要当前集合和形参集合的元素相同
Collection coll2 = new ArrayList();
coll2.add(123);
coll2.add(456);
System.out.println(coll.equals(coll2));//true
//12.hashCode():返回当前对象的哈希值
System.out.println(coll.hashCode());//5230
//13.集合--->数组:toArray()
Object[] array = coll.toArray();
for (Object obj : array) {
System.out.println(obj);
}
//14.数组--->集合:调用Arrays类的静态方法asList()
List<int[]> ints = Arrays.asList(new int[]{123, 345});
System.out.println(ints.size());//1
List<String> strings = Arrays.asList("AA", "BB", "CC");
System.out.println(strings);//[AA, BB, CC]
//15.iteratoriterator():返回Iterator接口的实例,用于遍历集合元素。
}
复制代码
3. Collection コレクションと配列の間の変換
//集合 --->数组:toArray()
Object[] arr = coll.toArray();
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
//拓展:数组 --->集合:调用Arrays类的静态方法asList(T ... t)
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list);
List arr1 = Arrays.asList(new int[]{123, 456});
System.out.println(arr1.size());//1
List arr2 = Arrays.asList(new Integer[]{123, 456});
System.out.println(arr2.size());//2
复制代码
Collection を使用してオブジェクトを格納するには、オブジェクトが属するクラスが次を満たす必要があります。
Collectionインターフェースの実装クラスのオブジェクトにデータobjを追加する場合は、objが格納されているクラスを書き換える必要があります
equals()
。
3. イテレータインターフェイスと foreach ループ
1. コレクションをトラバースする 2 つの方法:
① イテレータを使用する イテレータ
② foreach ループ(または拡張 for ループ)
2. java.utils パッケージで定義されたイテレータ インターフェイス: Iterator
2.1 説明:
Iterator オブジェクトはイテレーター (デザイン パターンの一種) と呼ばれ、主に Collection コレクション内の要素を走査するために使用されます。GOF では、イテレータ パターンを次のように定義しています。オブジェクトの内部詳細を公開せずに、コンテナ オブジェクト内の各要素にアクセスするメソッドを提供します。イテレータ パターンはコンテナのために生まれました。
2.2 機能:
コレクションの収集要素をトラバースします
2.3 インスタンスの取得方法:
coll.iterator()
イテレータインスタンスを返します
2.4 トラバーサルのコード実装:
Iterator iterator = coll.iterator();
//hasNext():判断是否还下一个元素
while(iterator.hasNext()){
//next():①指针下移 ②将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
复制代码
2.5 図:
2.6 イテレータでのremove()メソッドの使用:
- イテレータをテストする
remove()
- メソッドが呼び出されていないか 、
next()
最後のメソッド呼び出しnext()
後にメソッドが呼び出されたremove()
場合、remove を再度呼び出すと報告されますIllegalStateException
。 - 内部的に定義された
remove()
コレクション内の要素は、トラバーサル中に削除できます。このメソッドはコレクションを直接呼び出す方法とは異なります。remove()
コード例:
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry",20));
coll.add("Tom"
);
coll.add(false);
//删除集合中"Tom"
Iterator iterator = coll.iterator();
while (iterator.hasNext()){
// iterator.remove();
Object obj = iterator.next();
if("Tom".equals(obj)){
iterator.remove();
// iterator.remove();
}
}
//将指针重新放到头部,遍历集合
iterator = coll.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
复制代码
3. JDK 5.0 の新機能 - for ループの強化: (foreach ループ)
3.1 コレクションの走査の例:
@Test
public void test1(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry",20));
coll.add(new String("Tom"));
coll.add(false);
//for(集合元素的类型 局部变量 : 集合对象)
for(Object obj : coll){
System.out.println(obj);
}
}
复制代码
説明: イテレータはまだ内部的に呼び出されています。
3.2. 配列を走査する例:
@Test
public void test2(){
int[] arr = new int[]{1,2,3,4,5,6};
//for(数组元素的类型 局部变量 : 数组对象)
for(int i : arr){
System.out.println(i);
}
}
复制代码
4. コレクションサブインターフェース: リストインターフェース
1. 保存されたデータの特性:
連続した反復可能なデータを保存します。
- Java でデータを保存するために使用される配列の制限を考慮して、通常は配列の代わりに List を使用します。
- List コレクション クラスの要素は順序付けされており、反復可能であり、コレクション内の各要素には対応する順次インデックスがあります。
- List コンテナ内の要素は、コンテナ内の位置を記録する整数のシリアル番号に対応しており、コンテナ内の要素はシリアル番号に従ってアクセスできます。
- JDK AP の List インターフェイスの一般的な実装クラスは、ArrayList、LinkedList、Vector です。
2. 一般的な方法:
Collection コレクションから継承されたメソッドに加えて、List コレクションには、インデックスに基づいてコレクションの要素を操作するためのメソッドがいくつか追加されています。
void add(int index, Object ele)
: ele要素をインデックス位置に挿入しますboolean addAll(int index, Collection eles)
: eles 内のすべての要素をインデックス位置から追加します。Object get(int index)
: 指定したインデックス位置の要素を取得します。int indexOf(Object obj)
: コレクション内で obj が最初に出現する位置を返します。int lastIndexOf(Object obj)
: 現在のコレクション内で最後に出現した obj を返します。Object remove(int index)
: 指定されたインデックス位置にある要素を削除し、この要素を返します。Object set(int index, Object ele)
: 指定したインデックス位置の要素を ele に設定しますList subList(int fromIndex, int toIndex)
: サブコレクションを fromIndex から toIndex に返します。
要約:
- 増加:
add(Object obj)
- 削除:
remove(int index)
/remove(Object obj)
- 変化:
set(int index, Object ele)
- チェック:
get(int index)
- 入れる:
add(int index, Object ele)
- 長さ:
size()
- トラバーサル: ① イテレータ iterator メソッド ② foreach (拡張 for ループ) ③ 通常のループ
コード例:
@Test
public void test2(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",12));
list.add(456);
//int indexOf(Object obj):返回obj在集合中首次出现的位置。如果不存在,返回-1.
int index = list.indexOf(4567);
System.out.println(index);
//int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置。如果不存在,返回-1.
System.out.println(list.lastIndexOf(456));
//Object remove(int index):移除指定index位置的元素,并返回此元素
Object obj = list.remove(0);
System.out.println(obj);
System.out.println(list);
//Object set(int index, Object ele):设置指定index位置的元素为ele
list.set(1,"CC");
System.out.println(list);
//List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的左闭右开区间的子集合
List subList = list.subList(2, 4);
System.out.println(subList);
System.out.println(list);
}
@Test
public void test1(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",12));
list.add(456);
System.out.println(list);
//void add(int index, Object ele):在index位置插入ele元素
list.add(1,"BB");
System.out.println(list);
//boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
List list1 = Arrays.asList(1, 2, 3);
list.addAll(list1);
// list.add(list1);
System.out.println(list.size());//9
//Object get(int index):获取指定index位置的元素
System.out.println(list.get(0));
}
复制代码
3. 一般的な実装クラス:
3. 常用实现类:
|----Collection接口:单列集合,用来存储一个一个的对象
|----List接口:存储序的、可重复的数据。 -->“动态”数组,替换原的数组
|----ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
|----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
|----Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
复制代码
3.1 配列リスト
- ArrayList は List インターフェースの典型的な実装クラスであり、主要な実装クラスです。
- 基本的に、ArrayList はオブジェクト参照の「可変長」配列です。
- JDK 1.8 の前と後の Array Listi の実装の違いは何ですか?
- JDK 1.7: ArrayList は飢えた男のようなもので、初期容量 10 の配列を直接作成します。
- JDK 1.8: ArrayList は怠け者のようなもので、最初に長さ 0 の配列を作成し、最初の要素を追加するときに初期容量 10 の配列を作成します
Arrays.asList(...)
メソッドによって返される List コレクションは、ArrayList インスタンスでも Vector インスタンスでもありません。Arrays.asList(...)
戻り値は固定長のリストコレクションです。
コード例:
@Test
public void test1() {
Collection coll = new ArrayList();
coll.add(123);
coll.add(345);
coll.add(new User("Tom", 34));
coll.add(new User("Tom"));
coll.add(false);
//iterator()遍历ArrayList集合
Iterator iterator = coll.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
复制代码
3.2 リンクされたリスト
-
要素の挿入と削除を頻繁に行う場合は、より効率的な LinkedList クラスを使用することをお勧めします。
-
新しい方法:
void addFirst(Object obj)
void addLast(Object obj)
Object getFirst()
Object getlast)()
Object removeFirst()
Object removeLast()
-
Linkedlist: 二重リンク リスト内部で配列を宣言する代わりに、最初と最後の要素を記録するために Node タイプの最初と最後を定義します。同時に、Linkedlistにデータを保存するための基本構造として内部クラスNodeを定義します。データの保存に加えて、Node は 2 つの変数も定義します。
- prev: 変数は前の要素の位置を記録します
- next: 変数は次の要素の位置を記録します。
コード例:
@Test
public void test3(){
LinkedList linkedList = new LinkedList();
linkedList.add(123);
linkedList.add(345);
linkedList.add(2342);
linkedList.add("DDD");
linkedList.add("AAA");
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
复制代码
4. ソースコード解析(難易度)
4.1 ArrayList のソース コード分析:
4.1.1 JDK 7.0の場合
ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData
list.add(123);//elementData[0] = new Integer(123);
...
list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。
复制代码
-
デフォルトでは、拡張は元の容量の 1.5 倍であり、元のアレイのデータを新しいアレイにコピーする必要があります。
-
結論: 開発ではパラメータを含むコンストラクターを使用することをお勧めします。
ArrayList list = new ArrayList(int capacity)
4.1.2 JDK 8.0 での ArrayList の変更点:
ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没创建长度为10的数组
list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]
...
复制代码
その後の追加および拡張操作は JDK 7.0 と変わりません。
4.1.3 要約:
JDK 7.0 での ArrayList オブジェクトの作成はシングルトンのハングリー スタイルに似ていますが、JDK 8.0 での ArrayList オブジェクトの作成はシングルトンの遅延スタイルに似ており、配列の作成が遅れてメモリが節約されます。
4.2 LinkedList のソース コード分析:
LinkedList list = new LinkedList(); //内部声明了Node类型的first和last属性,默认值为null
list.add(123);//将123封装到Node中,创建了Node对象。
//其中,Node定义为:体现了LinkedList的双向链表的说法
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
复制代码
4.3 Vector のソースコード分析:
- Vector は古いコレクションであり、JDK 1.0 には含まれます。ほとんどの操作は ArrayList と同じですが、Vector がスレッドセーフである点が異なります。
- さまざまなリストの中で、デフォルトの選択肢として ArrayList を使用するのが最善です。挿入と削除が頻繁に行われる場合は、LinkedList を使用します。Vector は常に ArrayList よりも遅いため、使用は避けてください。
- JDK 7.0 および JDK 8.0 で Vector() コンストラクターを使用してオブジェクトを作成すると、長さ 10 の配列が最下層に作成されます。
- 拡張に関しては、デフォルトの拡張は元の配列の長さの 2 倍になります。
5. 保存された要素の要件:
追加されたオブジェクト、それが配置されているクラスは、equals() メソッドをオーバーライドする必要があります。
6. 面接での質問
ArrayList/LinkedList/Vector の類似点と相違点について質問してもいいですか? あなたの理解について話しますか?ArrayList の最下層は何ですか? 拡張機構?Vector と ArrayList の最大の違いは何ですか?
-
ArrayList と Linkedlist の類似点と相違点:
どちらもスレッドアンセーフですが、スレッドセーフな Vector に比べ、ArrayList の方が実行効率が高くなります。さらに、ArrayList は動的配列に基づいたデータ構造を実装し、Linkedlist はリンク リスト データ構造に基づいています。Linkedlist はポインタを移動する必要があるため、ランダム アクセスの get および set の場合は、Linkedlist よりも ArrayList の方が優れています。ArrayList はデータを移動する必要があるため、追加操作 (特に挿入) と削除操作の追加と削除の場合は Linkedlist が有利です。
-
ArrayList と Vector の違い:
Vector と ArrayList はほぼ同じですが、唯一の違いは、Vector が強力な同期クラスに属する同期クラス (synchronized) であることです。そのため、ArrayListに比べてオーバーヘッドが大きくなり、アクセスが遅くなります。通常の状況では、同期はプログラマが完全に制御できるため、ほとんどの Java プログラマは Vector ではなく ArrayList を使用します。Vector は展開するたびに 2 倍のサイズを要求しますが、ArrayList は 1.5 倍のサイズを必要とします。Vector にはサブクラス Stack もあります。
5. Collection サブインターフェース: Set インターフェースの概要
- Set インターフェイスは Collection のサブインターフェイスであり、set インターフェイスは追加のメソッドを提供しません。
- Set コレクションに同じ要素を含めることはできません。2 つの同一の要素を同じ Set コレクションに追加しようとすると、追加操作は失敗します。(主にフィルタリング操作と重複データの削除に使用されます)
- Set は、== 演算子を使用せずに、equals() メソッドに従って 2 つのオブジェクトが同じかどうかを判断します。
1. 保存されたデータの特性:
順序付けされていない反復不可能な要素を格納するために使用されます
HashSet を例に挙げます。
- 無秩序: ランダム性とは異なります。格納されるデータは、基になる配列の配列インデックスの順に追加されるのではなく、データのハッシュ値に従って決定されます。
- 非反復性: 追加された要素が、equals() で判定されたときに true を返せないこと、つまり、同じ要素は 1 つだけ追加できることを確認します。
2. 要素の追加プロセス: (HashSet を例にします)
要素 a を HashSet に追加し、まず要素 a が属するクラスの hashCode() メソッドを呼び出して要素 a のハッシュ値を計算し、次にこのハッシュ値に対して特定のアルゴリズムを通じて HashSet の基になる配列内の格納場所を計算します。 (つまり:インデックス位置)、判断
配列のこの位置に要素が既に存在するかどうか:
- この位置に他の要素がない場合、要素 a は正常に追加されます。---> ケース 1
- この位置に別の要素 b (またはリンク リスト形式の複数の要素) がある場合は、要素 a と要素 b のハッシュ値を比較します。
- ハッシュ値が同じでなければ、要素 a は正常に追加されます。---> ケース 2
- ハッシュ値が同じ場合は、要素 a が配置されているクラスの equals() メソッドを呼び出す必要があります。
- equals() は true を返し、要素 a の追加に失敗しました
- equals() が false を返すと、要素 a は正常に追加されます。---> ケース 3
追加が成功したケース 2 と 3 の場合、要素 a と指定されたインデックス位置に既に存在するデータが連結リスト形式で格納されます。
JDK 7.0: 要素 a は配列内に配置され、元の要素を指します。
JDK 8.0: 元の要素は配列内にあり、要素 a を指します。
要約: 浮き沈み
HashSet の最下層: 配列 + リンク リストの構造。(JDK 7.0より前)
3. 一般的な方法
Set インターフェイスには追加の新しいメソッドは定義されておらず、Collection で宣言されたすべてのメソッドが使用されます。
3.1 hashCode()の基本的な書き換え方法
-
プログラムの実行中に、
hashCode()
同じオブジェクトに対してメソッドを複数回呼び出すと、同じ値が返されるはずです。 -
2 つのオブジェクトのメソッド比較が true を返す場合 、
equals()
これら 2 つのオブジェクトのhashCode()
メソッドの戻り値も等しい必要があります。 -
equals()
hashCode 値の計算には、オブジェクト内のメソッド比較に使用される フィールドを使用する必要があります。
3.2 equals()
メソッドのオーバーライドの基本原則
-
カスタム Customer クラスを例に挙げると、いつ書き直す必要があるでしょうか
equals()
? -
クラスが独自の「論理的等価性」という概念を持っている場合、equals()を書き換える際には必ず書き換える必要があり、
hash Code()
クラスのequalsメソッドによれば(書き換え後)全く異なる2つのインスタンスが論理的に等しい場合もありますが、メソッドによればObject.hashCode()
、それらは単なる 2 つのオブジェクトです。 -
したがって、等価性に違反するオブジェクトは、等しいハッシュ コードを持つ必要があります。
-
結論:equalsメソッドを書き換える場合、通常はhashCodeメソッドも同時に書き換える必要があります。通常、hashCode の計算に関与するオブジェクトのプロパティも
equals()
計算に関与する必要があります。
3.3 Eclipse/IDEA ツールでの hashCode() の書き換え
Eclipse/DEA を例に挙げると、ツールを呼び出して自動的に書き換えたり equals()
、 hashCode()
質問: Eclipse/IDEA でハッシュ コード メソッドを書き換えると、なぜ 31 という数字が表示されるのですか?
-
係数を選択する場合は、できるだけ大きな係数を選択してください。計算されるハッシュアドレスが大きいほど、いわゆる「競合」が少なくなり、検索効率が向上するためです。(衝突が少ない)
-
また、31 は 5 ビットしか占有しないため、乗算によってデータがオーバーフローする可能性は低いです。
-
31 は i*31==(<<5)-1 で表すことができ、現在では多くの仮想マシンに関連する最適化が行われています。(アルゴリズムの効率を改善)
-
31 は素数です。素数の機能は、この素数に数値を乗算すると、最終結果は素数自体、被乗数、および 1 でのみ割り切れるということです。(衝突が少ない)
コード例:
@Override
public boolean equals(Object o) {
System.out.println("User equals()....");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() { //return name.hashCode() + age;
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
复制代码
4. 一般的な実装クラス:
|----Collection接口:单列集合,用来存储一个一个的对象
|----Set接口:存储无序的、不可重复的数据 -->高中讲的“集合”
|----HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
|----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历,对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
|----TreeSet:可以按照添加对象的指定属性,进行排序。
复制代码
4.1 ハッシュセット
- Hashset は Set インターフェイスの典型的な実装であり、この実装クラスは Set コレクションを使用するときにほとんどの場合に使用されます。
- HashSet は、ハッシュ アルゴリズムに従ってコレクション内の要素を格納するため、アクセス、検索、削除のパフォーマンスが優れています。
- HashSet には次の特徴があります。
- 要素の順序は保証されません
- HashSet はスレッドセーフではありません
- コレクション要素は null にすることもできます
- HashSet は、2 つの要素が等しいかどうかを判断するための基準を設定します。つまり、2 つのオブジェクトが hashCode() メソッドによって等しいかどうかが比較され、2 つのオブジェクトの equals() メソッドの戻り値も等しいかどうかが決まります。
- Set コンテナに格納されたオブジェクトの場合、対応するクラスは、オブジェクトの等価性ルールを実装するために、equals() メソッドと hashCode(Object obj) メソッドを書き換える必要があります。つまり、「等しいオブジェクトには等しいハッシュ コードが必要である」ということです。
コード例:
@Test
//HashSet使用
public void test1(){
Set set = new HashSet();
set.add(454);
set.add(213);
set.add(111);
set.add(123);
set.add(23);
set.add("AAA");
set.add("EEE");
set.add(new User("Tom",34));
set.add(new User("Jarry",74));
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
复制代码
4.2 リンクされたハッシュセット
-
LinkedhashSet は HashSet のサブクラスです
-
LinkedhashSet は、要素の hashCode 値に従って要素の格納場所を決定しますが、要素の順序を維持するために二重リンク リストも使用します。これにより、要素が挿入順に格納されているように見えます。
-
LinkedhashSet の挿入パフォーマンスは HashSet よりもわずかに低くなりますが、Set 内のすべての要素を反復処理する場合には良好なパフォーマンスが得られます。
-
LinkedhashSet では、コレクション要素の重複は許可されません。
グラフィック:
コード例:
@Test
//LinkedHashSet使用
public void test2(){
Set set = new LinkedHashSet();
set.add(454);
set.add(213);
set.add(111);
set.add(123);
set.add(23);
set.add("AAA");
set.add("EEE");
set.add(new User("Tom",34));
set.add(new User("Jarry",74));
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
复制代码
4.3 ツリーセット
-
Treeset は SortedSet インターフェイスの実装クラスであり、TreeSet はコレクション要素がソートされた状態であることを保証します。
-
TreeSet の最下層は赤黒ツリー構造を使用してデータを保存します
-
新しく追加されたメソッドは次のとおりです: (理解)
-
Comparator comparator()
-
Object first()
-
Object last()
-
Object lower(object e)
-
Object higher(object e)
-
SortedSet subSet(fromElement, toElement)
-
SortedSet headSet(toElement)
-
SortedSet tailSet(fromElement)
-
-
TreeSet には、自然な並べ替えとカスタム 並べ替えという 2 つの並べ替え方法があります。デフォルトでは、TreeSet は自然な順序付けを採用します。
赤黒樹形図:
赤黒ツリーの特徴: 整然としていて、クエリ効率がリストよりも速い
詳細な紹介: www.cnblogs.com/LiaHon/p/11…
コード例:
@Test
public void test1(){
Set treeSet = new TreeSet();
treeSet.add(new User("Tom",34));
treeSet.add(new User("Jarry",23));
treeSet.add(new User("mars",38));
treeSet.add(new User("Jane",56));
treeSet.add(new User("Jane",60));
treeSet.add(new User("Bruce",58));
Iterator iterator = treeSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
复制代码
5. ストレージ オブジェクトのクラスの要件:
5.1ハッシュセット/リンクされたハッシュセット:
- 要件: Set に追加されるデータ (主に HashSet、LinkedHashSet を指します) については、そのクラスが hashCode() とquals() を書き換える必要があります。
- 要件: 書き換えられた hashCode() とquals() は可能な限り一貫性があります: 等しいオブジェクトは等しいハッシュ コードを持つ必要があります。
2 つのメソッドをオーバーライドするためのヒント:quals() メソッドによる比較のためにオブジェクトで使用される Field は、hashCode 値の計算に使用する必要があります。
5.2 ツリーセット:
-
自然な並べ替えでは、2 つのオブジェクトが同じかどうかを比較するための標準は、
compareTo()
0 を返すこと です。equals()
-
カスタム並べ替えでは、2 つのオブジェクトが同じかどうかを比較するための標準は「
compare()
0 を返す」 です。equals()
6. TreeSetの使用
6.1 使用説明書:
-
TreeSet に追加されるデータは、同じクラスのオブジェクトである必要があります。
-
2 つの並べ替え方法: 自然な並べ替え (Comparable インターフェイスの実装とカスタム 並べ替え (Comparator))
6.2 一般的に使用される並べ替え方法:
方法 1: 自然な並べ替え
-
compareTo(object obj)
自然な並べ替え: TreeSetは、コレクション要素のメソッドを呼び出して 要素間のサイズ関係を比較し、コレクション要素を昇順に並べます (デフォルト)。 -
オブジェクトを Treeset に追加しようとする場合、オブジェクトのクラスは Comparable インターフェイスを実装する必要があります。
- Comparable を実装するクラスは
compareTo(Object obj)
メソッドを実装する必要があり、2 つのオブジェクトはcompareTo(Object obj)
メソッドの戻り値によって比較されます。
- Comparable を実装するクラスは
-
Comparable の典型的な実装:
-
BigDecimal、BigInteger、および数値型に対応するすべてのラッパー クラス: 対応する数値サイズに従って比較します。
-
キャラクター:キャラクターごとにユニック!比較する ode 値
-
ブール値: true に対応するラッパー クラス インスタンスは、false に対応するラッパー クラス インスタンスより大きいです。
-
文字列: 文字列内の文字の Unicode 値で比較します。
-
日付、時刻: 後の時刻と日付は前の時刻と日付よりも大きくなります
-
-
TreeSet に要素を追加する場合、最初の要素のみが比較メソッドを必要としません 。後から追加されたすべての要素は 比較のためにメソッドを
compareTo()
呼び出します 。compareTo()
-
同じクラスの 2 つのインスタンスのみがサイズを比較するため、同じクラスのオブジェクトを TreeSet に追加する必要があります。TreeSet コレクションの場合、2 つのオブジェクトが等しいかどうかを判断する唯一の基準は、
compareTo(Object obj)
メソッド比較による 2 つのオブジェクトの戻り値です。 -
オブジェクトを TreeSet に配置し、そのオブジェクトに対応する equals() メソッドを書き直す必要がある場合は、メソッドがそのメソッドと
compareTo(Object obj)
同じ結果になることを確認する必要があります。つまり、equals() メソッドの比較を通じて 2 つのオブジェクトが true を返した場合です。 、次に、compareTo(object ob)
メソッド比較を使用します。 Should return 0。そうしないと、理解するのが難しいです。
@Test
public void test1(){
TreeSet set = new TreeSet();
//失败:不能添加不同类的对象
// set.add(123);
// set.add(456);
// set.add("AA");
// set.add(new User("Tom",12));
//举例一:
// set.add(34);
// set.add(-34);
// set.add(43);
// set.add(11);
// set.add(8);
//举例二:
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Jack",33));
set.add(new User("Jack",56));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
复制代码
方法 2: カスタム並べ替え
-
TreeSet を自然に並べ替えるには、要素が属するクラスが Comparable インターフェイスを実装している必要があります。要素が属するクラスが Comparable インターフェイスを実装していない場合、または要素を昇順に並べたくない場合 (デフォルトでは) )、または他の属性サイズで並べ替えたい場合は、カスタム並べ替えの使用を検討してください。カスタム並べ替えは、Comparator インターフェイスを通じて実装されます。Compare(T o1, To2) メソッドを書き直す必要があります。
-
このメソッドを使用して
int compare(T o1,T o2)
、o1 と o2 のサイズを比較します。メソッドが正の整数を返す場合は、o1 が o2 より大きいことを意味します。0 を返す場合は、それらが等しいことを意味します。負の整数を返す場合は、次のことを意味します。 o1はo2より小さいです。 -
カスタム並べ替えを実装するには、Comparator インターフェイスを実装するインスタンスを正式パラメータとして TreeSet コンストラクターに渡す必要があります。
-
この時点では、同じタイプのオブジェクトのみをツリーセットに追加できます。それ以外の場合は
ClassCastException
例外が発生します -
カスタム ソートを使用して 2 つの要素が等しいかどうかを判断するための標準は、コンパレータで 2 つの要素を比較すると 0 が返されます。
@Test
public void test2(){
Comparator com = new Comparator() {
//照年龄从小到大排列
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return Integer.compare(u1.getAge(),u2.getAge());
}else{
throw new RuntimeException("输入的数据类型不匹配");
}
}
};
TreeSet set = new TreeSet(com);
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Mary",33));
set.add(new User("Jack",33));
set.add(new User("Jack",56));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
复制代码
6. マップインターフェース
- マップとコレクションは並列して存在します。マッピング関係を使用してデータを保存するために使用されます: キーと値
- Map のキーと値は両方とも、任意の参照型のデータにすることができます
- Map 内のキーはセットに格納され、重複は許可されません。つまり、同じ Map オブジェクトに対応するクラスは メソッドを
hashCode()
書き換える 必要があります。equals()
- String クラスは、Map の「キー」としてよく使用されます。
- キーと値の間には一方向の 1 対 1 の関係があります。つまり、一意で明確な値は、指定されたキーを通じて常に見つけることができます。
- Map インターフェイスの共通実装クラス: HashMap、TreeMap、LinkedHashMap、および Properties。その中でも、HashMap は、Map インターフェースの実装クラスとして最も頻繁に使用されます。
1. 共通実装クラス構造
|----Map:双列数据,存储key-value对的数据 ---类似于高中的函数:y = f(x)
|----HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
|----LinkedHashMap:保证在遍历map元素时,可以照添加的顺序实现遍历。
原因:在原的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
对于频繁的遍历操作,此类执行效率高于HashMap。
|----TreeMap:保证照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
底层使用红黑树
|----Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
|----Properties:常用来处理配置文件。key和value都是String类型
HashMap的底层: 数组+链表 (JDK 7.0及之前)
数组+链表+红黑树 (JDK 8.0以后)
复制代码
1.1 ハッシュマップ
-
HashMap は、Map インターフェイスの最も頻繁に使用される実装クラスです。
-
Null キーと Null 値が許可され、HashSet と同様に、マッピングの順序は保証されません。
-
すべてのキーのコレクションはセットであり、順序付けがなく、反復不可能です。したがって、キーが配置されているクラスは、equals() と hashCode() を書き直す必要があります。
-
すべての値のコレクションは、コレクション: 順序付けされておらず、反復可能です。したがって、値が配置されているクラスは、equals() のように書き換える必要があります。
-
Key-Value がエントリを構成します
-
すべてのエントリのセットは Set です: 順序付けされておらず、反復不可能です
-
HashMap が 2 つのキーが等しいと判断する基準は、2 つのキーが
equals()
メソッドを通じて true を返し、hashCode の値も等しいということです。 -
HashMap が 2 つの値が等しいと判断する基準は、
equals()
メソッドを通じて 2 つの値が true を返すことです。
コード例:
@Test
public void test1(){
Map map = new HashMap();
map.put(null,123);
}
复制代码
1.2 リンクされたハッシュマップ
- LinkedHashMap は HashMap を継承するため、LinkedHashMap の基礎となる構造は HashMap の構造と同じです。
- 違いは、LinkedHashMap が内部に Entry を提供し、HashMap 内の Node を置き換えることです。
- Linkedhash Set と同様に、LinkedHashMap は Map の反復順序を維持できます。反復順序は Key-Value ペアの挿入順序と一致します。
コード例:
@Test
public void test2(){
Map map = new LinkedHashMap();
map.put(123,"AA");
map.put(345,"BB");
map.put(12,"CC");
System.out.println(map);
}
复制代码
1.3 ツリーマップ
-
TreeMap が Key-Value ペアを格納する場合、Key-Value ペアに従って並べ替える必要があります。TreeMap は、すべての Key-Value ペアが順序付けられた状態であることを保証できます。
-
TreeSet の最下層は赤黒ツリー構造を使用してデータを保存します
-
TreeMap のキーのソート:
- 自然な並べ替え: TreeMap のすべてのキーは Comparable インターフェイスを実装する必要があり、すべてのキーは同じクラスのオブジェクトである必要があります。そうでない場合は、ClassCastException() がスローされます。
- カスタム並べ替え: TreeMap を作成するときに、TreeMap 内のすべてのキーを並べ替える役割を担う Comparator オブジェクトを渡します。現時点では、マップのキーが Comparable インターフェイスを実装する必要はありません。
-
TreeMap は、2 つのキーが等しいかどうかの基準を判断します。2 つのキーは、compareTo() メソッドまたは Compare() メソッドを通じて 0 を返します。
1.4 ハッシュテーブル
-
Hashtable は、JDK1.0 で提供される古代の Map 実装クラスです。HashMap とは異なり、Hashtable はスレッドセーフです。
-
Hashtable の実装原理は HashMap と同じであり、機能も同じです。最下層はハッシュテーブル構造を使用し、クエリ速度が速く、多くの場合互換的に使用できます。
-
HashMap とは異なり、Hashtable ではキーと値として null を使用できません。
-
HashMap と同様に、Hashtable はその中の Key-Value ペアの順序を保証できません。
-
Hashtable は 2 つのキーが等しい、2 つの値が等しいと判断します。これは HashMap と一致します。
1.5 プロパティ
-
Properties クラスは Hashtable のサブクラスであり、プロパティ ファイルの処理に使用されます。
-
プロパティファイルのキーと値はすべて文字列型なので、プロパティのキーと値もすべて文字列型になります
-
setProperty(String key,String value)
データにアクセスするときは、メソッドとgetProperty(String key)
メソッドを使用することをお勧めします
コード例:
//Properties:常用来处理配置文件。key和value都是String类型
public static void main(String[] args) {
FileInputStream fis = null;
try {
Properties pros = new Properties();
fis = new FileInputStream("jdbc.properties");
pros.load(fis);//加载流对应的文件
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println("name = " + name + ", password = " + password);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
复制代码
2. ストレージ構造の理解:
- Map 内のキー: 順序付けされておらず、反復不可能で、Set に格納されているキーを使用します ---> キーが配置されているクラスは、equals() と hashCode() を書き換える必要があります (HashMap を例にします)
- マップ内の値: 順序付けされておらず、反復可能、コレクションを使用して値を保存します ---> 値が配置されているクラスは、equals() を書き直す必要があります
- キーと値のペア: キーと値が Entry オブジェクトを構成します。
- マップ内のエントリ: 順序付けされておらず、繰り返し不可。エントリを保存するには Set を使用します。
3. 一般的な方法
3.1 追加、削除、変更操作:
Object put(Object key,Object value)
: 指定されたキーと値を現在のマップ オブジェクトに追加 (または変更)void putAll(Map m)
: m 内のすべてのキーと値のペアを現在のマップに保存しますObject remove(Object key)
: 指定されたキーのキーと値のペアを削除し、値を返します。void clear()
: 現在のマップ内のすべてのデータをクリアします
コード例:
@Test
public void test1() {
Map map = new HashMap();
//Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
map.put("AA",123);
map.put("ZZ",251);
map.put("CC",110);
map.put("RR",124);
map.put("FF",662);
System.out.println(map);//{AA=123, ZZ=251, CC=110, RR=124, FF=662}
//Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
map.put("ZZ",261);
System.out.println(map);//{AA=123, ZZ=261, CC=110, RR=124, FF=662}
//void putAll(Map m):将m中的所有key-value对存放到当前map中
HashMap map1 = new HashMap();
map1.put("GG",435);
map1.put("DD",156);
map.putAll(map1);
System.out.println(map);//{AA=123, ZZ=261, CC=110, RR=124, FF=662, GG=435, DD=156}
//Object remove(Object key):移除指定key的key-value对,并返回value
Object value = map.remove("GG");
System.out.println(value);//435
System.out.println(map);//{AA=123, ZZ=261, CC=110, RR=124, FF=662, DD=156}
//void clear():清空当前map中的所有数据
map.clear();
System.out.println(map.size());//0 与map = null操作不同
System.out.println(map);//{}
}
复制代码
3.2 要素クエリの操作:
Object get(Object key)
: 指定したキーに対応する値を取得しますboolean containsKey(Object key)
: 指定されたキーを含むかどうかboolean containsValue(Object value)
: 指定した値を含むかどうかint size()
: マップ内のキーと値のペアの数を返します。boolean isEmpty()
: 現在のマップが空かどうかを判断しますboolean equals(Object obj)
: 現在のマップとパラメータ オブジェクト obj が等しいかどうかを判断します。
コード例:
@Test
public void test2() {
Map map = new HashMap();
map.put("AA", 123);
map.put("ZZ", 251);
map.put("CC", 110);
map.put("RR", 124);
map.put("FF", 662);
System.out.println(map);//{AA=123, ZZ=251, CC=110, RR=124, FF=662}
//Object get(Object key):获取指定key对应的value
System.out.println(map.get("AA"));//123
//boolean containsKey(Object key):是否包含指定的key
System.out.println(map.containsKey("ZZ"));//true
//boolean containsValue(Object value):是否包含指定的value
System.out.println(map.containsValue(123));//true
//int size():返回map中key-value对的个数
System.out.println(map.size());//5
//boolean isEmpty():判断当前map是否为空
System.out.println(map.isEmpty());//false
//boolean equals(Object obj):判断当前map和参数对象obj是否相等
Map map1 = new HashMap();
map1.put("AA", 123);
map1.put("ZZ", 251);
map1.put("CC", 110);
map1.put("RR", 124);
map1.put("FF", 662);
System.out.println(map.equals(map1));//true
}
复制代码
3.3 メタビューの操作方法:
Set keySet()
: すべてのキーで構成される Set コレクションを返します。Collection values()
: すべての値で構成されるコレクション セットを返します。Set entrySet()
: すべてのキーと値のペアで構成される Set コレクションを返します。
コード例:
@Test
public void test3() {
Map map = new HashMap();
map.put("AA", 123);
map.put("ZZ", 251);
map.put("CC", 110);
map.put("RR", 124);
map.put("FF", 662);
System.out.println(map);//{AA=123, ZZ=251, CC=110, RR=124, FF=662}
//遍历所有的key集:Set keySet():返回所有key构成的Set集合
Set set = map.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("--------------");
//遍历所有的value集:Collection values():返回所有value构成的Collection集合
Collection values = map.values();
for (Object obj :
values) {
System.out.println(obj);
}
System.out.println("---------------");
//Set entrySet():返回所有key-value对构成的Set集合
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
//方式一:
while (iterator1.hasNext()) {
Object obj = iterator1.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
System.out.println("--------------");
//方式二:
Set keySet = map.keySet();
Iterator iterator2 = keySet.iterator();
while (iterator2.hasNext()) {
Object key = iterator2.next();
Object value = map.get(key);
System.out.println(key + "==" + value);
}
}
复制代码
概要: 一般的な方法:
- に追加:
put(Object key,Object value)
- 消去:
remove(Object key)
- 改訂:
put(Object key,Object value)
- お問い合わせ:
get(Object key)
- 長さ:
size()
- トラバース :
keySet()
//values()
entrySet()
4. メモリ構造の説明:(難点)
4.1 JDK 7.0 の HashMap 実装原則:
4.1.1 HashMap のストレージ構造:
JDK 7.0以前のバージョン: HashMapは配列+リンクリスト構造(アドレスリンクリスト方式)
JDK 8.0 バージョン以降: HashMap は配列 + リンク リスト + 赤黒ツリーの実装です
4.1.2 オブジェクトの作成と追加のプロセス:
HashMap map = new HashMap()
:
インスタンス化後、基礎となるものは長さ 16 の 1 次元配列を作成します Entry[] table
。
...複数の put を実行した可能性があります...
map.put(key1,value1)
:
- まず、
hashCode()
key1が属するクラスの計算key1ハッシュ値を呼び出し、所定のアルゴリズムでハッシュ値を計算し、Entry配列内の格納位置を取得します。 - この位置のデータが空であれば、この時点で key1-value1 は正常に追加されます。---- 状況 1
- この場所のデータが空でない場合 (この場所に 1 つ以上のデータ (リンク リストの形式で) があることを意味します)、key1 を 1 つ以上の既存データのハッシュ値と比較します。
- key1 のハッシュ値が既存データのハッシュ値と異なる場合、key1-value1 は正常に追加されます。---- 状況 2
- key1 のハッシュ値が既存のデータのハッシュ値 (key2-value2) と同じである場合は、比較を続けます。key1 が配置されているクラスの equals(key2) メソッドを呼び出して比較します。
- false を返した場合
equals()
: この時点で key1-value1 は正常に追加されます。---- 状況 3 - trueの場合
equals()
: value2 を value1 に置き換えます。
- false を返した場合
補足:ケース2、ケース3について: このとき、key1-value1と元データはリンクリスト形式で保存されます。
連続加算の過程では容量拡張の問題が発生し、臨界値を超えると(格納先が空でない場合)、容量が拡張されます。デフォルトの拡張方法: 元の容量の 2 倍に容量を拡張し、元のデータをコピーします。
4.1.3 HashMapの拡張
HashMap 内の要素が増えると、配列の長さが固定されるため、ハッシュ競合の可能性がますます高くなります。したがって、クエリ効率を向上させるには、HashMap 配列を拡張する必要があります。HashMap 配列を拡張した後、元の配列内のデータを新しい配列内の位置を再計算して配置する必要があります。これがサイズ変更です。
4.1.4 HashMapの展開タイミング
HashMap の要素の数が配列のサイズ (配列内の数ではなく、配列の全長) *loadFactor を超えると、配列は拡張されます。loadFactor () のデフォルト値は 0.75 です。妥協の値DEFAULT_LOAD_ FACTOR
。つまり、デフォルトでは配列サイズ ( DEFAULT INITIAL CAPACITY
) は 16 ですが、HashMap 内の要素の数が 16 * 0.75=12 (この値はコード内のしきい値であり、臨界値とも呼ばれます) を超えると、配列 サイズは 2 * 16 = 32 (つまり 2 倍) に拡張され、配列内の各要素の位置が再計算されます。これは非常にパフォーマンスを消費する操作です。 HashMap を作成し、要素の数を事前に設定すると、HashMap のパフォーマンスを効果的に向上させることができます。
4.2 JDK 8.0 における HashMap の基本的な実装原則:
4.2.1 HashMap のストレージ構造:
HashMap の内部ストレージ構造は、実際には配列 + リンク リスト + 赤黒ツリーの組み合わせです。
4.2.2 HashMap に要素を追加するプロセス:
HashMap をインスタンス化するとき、initialCapacity とloadFactor が初期化されます。マッピング関係の最初のペアを配置するとき、システムは、initialCapacity の長さを持つ Node 配列を作成します。この長さは、ハッシュ テーブルの容量と呼ばれます。この中で、要素が配置される場所配列に格納できる要素は「バケット」と呼ばれます。各バケットには独自のインデックスがあり、システムはインデックスに基づいてバケット内の要素を迅速に検索できます。
各バケットには要素、つまり Node オブジェクトが格納されますが、各 Noe オブジェクトは次の要素を指す参照変数を保持できるため、バケット内で Node チェーンを生成することができます。TreeNode オブジェクトにすることもできます。各 Tree ノード オブジェクトは左右に 2 つのリーフ ノードを持つことができるため、バケット内に TreeNode ツリーを生成することができます。新しく追加された要素は、リンクされたリストの最後、またはツリーの葉ノードとして使用されます。
4.2.3 HashMap の拡張メカニズム:
- HashMpl のいずれかのチェーンのオブジェクト数が 8 に達しない場合は、JDK 7.0 より前の展開方法と同じになります。
- HashMap のいずれかのチェーンのオブジェクト数が 8 に達したとき、その時点で容量が 64 に達していなければ、HashMap がまず容量を拡張し、64 に達している場合はチェーンがツリーになり、ノード タイプは、ノードがツリー ノード タイプになることによって決まります。もちろん、マッピング関係が削除された後の次のリサイズメソッドでツリー内のノード数が 6 未満であると判断された場合は、ツリーは再度連結リストに変換されます。
4.2.4 JDK 8.0 と JDK 7.0 の間の HashMap の下部の変更点:
-
new HashMap()
: 最下層は長さ 16 の配列を作成しませんでした -
JDK 8.0 の基礎となる配列は
Node[]
次のとおり です。Entry[]
-
put() メソッドが初めて呼び出されるとき、基礎となる層は長さ 16 の配列を作成します。
-
JDK 7.0 の基礎となる構造は、配列 + リンク リストのみです。JDK 8.0 の基礎となる構造: 配列 + リンク リスト + 赤黒ツリー。
- リンクリストを形成する場合、上下になります(jdk7:新しい要素は古い要素を指します。jdk8:古い要素は新しい要素を指します)。
- 連結リスト形式の配列の特定のインデックス位置の要素数が 8 を超え、現在の配列の長さが 64 を超える場合、このインデックス位置のすべてのデータが赤黒ツリーに格納されます。その代わり。
4.3 HashMap の最下層の典型的なプロパティの説明:
DEFAULT_INITIAL_CAPACITY
: HashMap のデフォルトの容量、16DEFAULT_LOAD_FACTOR
: HashMap のデフォルトの負荷係数: 0.75threshold
: 膨張の臨界値、= 容量 * フィルファクター: 16 * 0.75 => 12TREEIFY_THRESHOLD
: バケット内のリンクされたリストの長さがデフォルト値より大きいため、赤黒ツリーに変換されます: JDK 8.0 で導入されましたMIN_TREEIFY_CAPACITY
: バケット内のノードがツリー化されている場合の最小ハッシュ テーブル容量: 64
4.4 LinkedHashMap の基本的な実装原理
- LinkedHashMap は HashMap を継承するため、LinkedHashMap の基礎となる構造は HashMap の構造と同じです。
- 違いは、LinkedHashMap が内部に Entry を提供し、HashMap 内の Node を置き換えることです。
- Linkedhash Set と同様に、LinkedHashMap は Map の反復順序を維持できます。反復順序は Key-Value ペアの挿入順序と一致します。
HashMap の内部クラス Node ソース コード:
static class Node<K,V> implements Map.Entry<K,V>{
final int hash;
final K key;
V value;
Node<K,V> next;
}
复制代码
LinkedHashM の内部クラス エントリのソース コード:
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;//能够记录添加的元素的先后顺序
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
复制代码
5. ツリーマップの使用
TreeMap にキーと値を追加します。キーは同じクラスによって作成される必要があります。オブジェクトはキーに従って並べ替えられる必要があります: 自然な並べ替え、カスタム 並べ替え
コード例:
//自然排序
@Test
public void test() {
TreeMap map = new TreeMap();
User u1 = new User("Tom", 23);
User u2 = new User("Jarry", 18);
User u3 = new User("Bruce", 56);
User u4 = new User("Davie", 23);
map.put(u1, 98);
map.put(u2, 16);
map.put(u3, 92);
map.put(u4, 100);
Set entrySet = map.entrySet();
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
//定制排序:按照年龄大小排
@Test
public void test2() {
TreeMap map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof User && o2 instanceof User) {
User u1 = (User) o1;
User u2 = (User) o2;
return Integer.compare(u1.getAge(), u2.getAge());
}
throw new RuntimeException("输入数据类型错误");
}
});
User u1 = new User("Tom", 23);
User u2 = new User("Jarry", 18);
User u3 = new User("Bruce", 56);
User u4 = new User("Davie", 23);
map.put(u1, 98);
map.put(u2, 16);
map.put(u3, 92);
map.put(u4, 100);
Set entrySet = map.entrySet();
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
复制代码
6. プロパティを使用して構成ファイルを読み取ります。
コード例:
//Properties:常用来处理配置文件。key和value都是String类型
public static void main(String[] args) {
FileInputStream fis = null;
try {
Properties pros = new Properties();
fis = new FileInputStream("jdbc.properties");
pros.load(fis);//加载流对应的文件
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println("name = " + name + ", password = " + password);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
复制代码
7. 面接での質問
- HashMap の基本的な実装原理は何ですか?
- HashMap と Hashtable の類似点と相違点は?
- CurrentHashMap と Hashtable の類似点と相違点は?
- 負荷係数値の大きさ、HashMap への影響は?
- 負荷係数のサイズによって、HashMap のデータ密度が決まります。
- 負荷率が大きいほど、密度が高く、衝突の可能性が高くなります。また、配列内のリンク リストが増大しやすくなり、その結果、クエリまたは挿入中の比較の数が増加し、パフォーマンスが低下します。
- 負荷係数が小さいほど、拡張のトリガーが容易になり、データ密度が小さくなります。つまり、衝突の可能性が低くなり、配列内のリンク リストが短くなり、クエリと挿入中の比較の数が少なくなります。小さいほどパフォーマンスは高くなります。ただし、一定のコンテンツスペースが無駄になります。また、頻繁に拡張するとパフォーマンスにも影響しますので、大きめの領域を初期化してプリセットすることをお勧めします。
- 他言語の参考文献や研究経験から、負荷率を0.7~0.75に設定することが考えられますが、このときの平均検索長は一定に近くなります。
7、収集ツールの使用
1.機能:
Collections は、Set、Lit、Map などのコレクションを操作するためのツール クラスです。
Collections は、コレクション要素の並べ替え、クエリ、変更を行うための一連の静的メソッドを提供します。また、不変のコレクション オブジェクトを設定し、コレクション オブジェクトの同期制御を実現するためのメソッドも提供します。
2. 一般的な方法:
2.1 ソート操作
reverse(List)
: リスト内の要素の順序を逆にします。shuffle(List)
: List コレクションの要素をランダムに並べ替えますsort(List)
: 指定された List コレクションの要素を、要素の自然な順序に従って昇順に並べ替えます。sort(List,Comparator)
: 指定された Comparator によって生成された順序に従って List コレクション要素を並べ替えます。swap(List,int, int)
: 指定されたリストコレクションの i の要素と j の要素を交換します。
コード例:
@Test
public void test1() {
List list = new ArrayList();
list.add(123);
list.add(43);
list.add(765);
list.add(-97);
list.add(0);
System.out.println(list);//[123, 43, 765, -97, 0]
//reverse(List):反转 List 中元素的顺序
Collections.reverse(list);
System.out.println(list);//[0, -97, 765, 43, 123]
//shuffle(List):对 List 集合元素进行随机排序
Collections.shuffle(list);
System.out.println(list);//[765, -97, 123, 0, 43]
//sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
Collections.sort(list);
System.out.println(list);//[-97, 0, 43, 123, 765]
//swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
Collections.swap(list,1,4);
System.out.println(list);//[-97, 765, 43, 123, 0]
}
复制代码
2.2 検索と置換
Object max(Collection)
: 要素の自然な順序に従って、指定されたコレクション内の最大の要素を返します。Object max(Collection,Comparator)
: Comparator で指定された順序に従って、指定されたコレクション内の最大の要素を返します。Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object)
: 指定されたコレクション内の指定された要素の出現数を返します。void copy(List dest,List src)
: srcの内容をdestにコピーします。boolean replaceAll(List list,Object oldVal,Object newVal)
: List オブジェクトのすべての古い値を新しい値に置き換えます
コード例:
@Test
public void test2(){
List list = new ArrayList();
list.add(123);
list.add(123);
list.add(123);
list.add(43);
list.add(765);
list.add(-97);
list.add(0);
System.out.println(list);//[123, 43, 765, -97, 0]
//Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Comparable max = Collections.max(list);
System.out.println(max);//765
//Object min(Collection)
Comparable min = Collections.min(list);
System.out.println(min);//-97
//int frequency(Collection,Object):返回指定集合中指定元素的出现次数
int frequency = Collections.frequency(list,123);
System.out.println(frequency);//3
//void copy(List dest,List src):将src中的内容复制到dest中
List dest = Arrays.asList(new Object[list.size()]);
System.out.println(dest.size());//7
Collections.copy(dest,list);
System.out.println(dest);//[123, 123, 123, 43, 765, -97, 0]
//boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
}
复制代码
2.3 同期制御
Collections クラスは、 synchronizedXxx()
指定されたコレクションをスレッド同期コレクションにラップできる複数のメソッドを提供し、複数のスレッドがコレクションに同時にアクセスする場合のスレッド セーフティの問題を解決します。
コード例:
@Test
public void test3() {
List list = new ArrayList();
list.add(123);
list.add(123);
list.add(123);
list.add(43);
list.add(765);
list.add(-97);
list.add(0);
System.out.println(list);//[123, 43, 765, -97, 0]
//返回的list1即为线程安全的List
List list1 = Collections.synchronizedList(list);
System.out.println(list1);//[123, 123, 123, 43, 765, -97, 0]
}
复制代码
8. データ構造の簡単な説明
今後、データ構造について語るシリーズを開設する予定です。
1. データ構造の概要
データ構造 (データ構造は、コンピュータのハードウェアとソフトウェアに密接に関連する学問です。その研究の焦点は、コンピュータ内でデータをどのように整理して保存し、コンピュータ プログラミングの分野で効率的に使用するかにあります。含まれる内容には以下が含まれます: データの論理関係、データの保存構造、ソートアルゴリズム(Algorithm)、検索(または検索)など。
2. データ構造とアルゴリズムの理解
プログラムが所定のタスクを迅速かつ効率的に完了できるかどうかは、データ構造が正しく選択されているかどうかに依存し、プログラムが問題を明確かつ正確に解決できるかどうかはアルゴリズムに依存します。
したがって、誰もが次のように考えます: 「アルゴリズム + データ構造 = プログラム」 (出典: パスカルの父ニクラウス ヴィルス)
概要: アルゴリズムは実際的な問題を解決するように設計されており、データ構造はアルゴリズムが対処する必要がある問題の媒介です。
3. データ構造の研究対象
3.1 データ間の論理構造
セット構造
1 対 1: 線形構造
1対多: ツリー構造
多対多: グラフ構造
3.2 データストレージ構造:
線形リスト(シーケンスリスト、リンクリスト、スタック、キュー)ツリー図
説明: シーケンシャル リストとリンク リストを基本データ構造 (または実際のデータ構造) とみなすのが通例であり、スタック、キュー、ツリー、およびグラフを ADT と呼ばれる抽象データ型とみなすのが通例です
4. マインドマップ:
マインドマップのダウンロードアドレス: gitee.com/realbruce/b…
データ構造とアルゴリズム: