まず、コレクションフレームワークの簡略図を見てみましょう。↓↓↓
(1)、コレクションインターフェイス
コレクションは、List、Set、Queue(first in first out queue)、Deque(double linked list)、およびその他のインターフェースの親インターフェースです。
[注:コレクションとマップの間に関係はありません。コレクションにはオブジェクトが1つずつ含まれ、マップにはキーと値のペアが含まれます。]
[注:DequeはQueueを継承し、間接的にCollectionを継承します]
(2)、コレクションクラス
コレクションはクラスであり、コンテナのツールクラスです。アレイがアレイのツールクラスであるのと同様に、コンテナに対していくつかの操作を実行することもできます。
メソッド名のキーワード | 特徴 |
逆行する | 逆行する |
シャッフル | 混乱(注文の混乱) |
ソート | ソート |
スワップ | 指定された添え字位置でデータを交換します |
回転する | コレクション内のデータを、指定した単位の長さだけ右にスクロールします |
同期リスト | スレッドセーフでないコレクションをスレッドセーフコレクションに変換する |
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsTest {
public static void main(String[] args) {
// 初始化List
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i+1);
}
// 原List
System.out.println("原list:\t\t\t" + list);
System.out.println("******************************");
// 反转
Collections.reverse(list);
System.out.println("反转后的list:\t\t" + list);
System.out.println("******************************");
// 混淆
Collections.shuffle(list);
System.out.println("混淆后的list:\t\t" + list);
System.out.println("******************************");
// 排序
Collections.sort(list);
System.out.println("排序后的list:\t\t" + list);
System.out.println("******************************");
// 交换两个位置的数据
Collections.swap(list, 0, 1);
System.out.println("交换0和1下标的数据后的list:\t" + list);
System.out.println("******************************");
// 先恢复原list
Collections.swap(list, 0, 1);
System.out.println("恢复后的list:\t\t" + list);
// 滚动
Collections.rotate(list, 3);
System.out.println("向右滚动3个单位后的list:\t" + list);
System.out.println("******************************");
// 线程安全化
List<Integer> newList = Collections.synchronizedList(list);
// 把原来不是线程安全的list转换为了现在线程安全的newList
}
}
出力結果:
(3)リストインターフェイスとArrayListクラス
Listコレクション内のオブジェクトは特定の順序で配置され、内部のコンテンツを繰り返すことができます。
Listインターフェイスによって実装されるクラスは、ArrayList(動的配列を実現するため)、Vector(動的配列を実現するため)、LinkedList(リンクリストを実現するため)、Stack(スタックを実現するため)です。
[1]、アレイの使用の制限
複数のオブジェクトを保存する場合は、配列を使用できますが、配列には制限があります。!!
例:長さが10の配列を宣言すると、未使用の配列は無駄になり、10を超える数は収まりません!!!
[2]、ArrayList
配列の制限を解決するために、コンテナクラスの概念が導入されています。最も一般的なコンテナクラスは次のとおりです。ArrayList
コンテナの容量「容量」は、オブジェクトが増加すると自動的に増加します。
[3] ArrayListの一般的なメソッド
メソッド名のキーワード | 特徴 |
含まれています | 存在するかどうかを確認します |
追加 | オブジェクトを追加 |
全て追加する | 別のコンテナにすべてのオブジェクトを追加します |
削除する | オブジェクトを削除する |
晴れ | 空の |
セットする | 特定の位置でオブジェクトを置き換える(再割り当て) |
取得する | 指定された場所でオブジェクトを取得します |
の指標 | オブジェクトの場所を取得します |
toArray | 配列に変換 |
サイズ | サイズを取得 |
[4]、イテレータートラバーサル
Iteratorを使用してコレクション内の要素をトラバースします。原則は次のとおりです。↓↓↓
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 5; i++) {
list.add("String " + i);
}
System.out.println("最初的list为:" + list);
// 获取List的迭代器
Iterator<String> iterator = list.iterator();
//从最开始的位置判断"下一个"位置是否有数据
//如果有就通过next取出来,并且把指针向下移动
//直到"下一个"位置没有数据
System.out.println("--------使用while的iterator-------");
// 利用while循环遍历
while(iterator.hasNext()) {
String str = iterator.next();
System.out.println(str);
}
System.out.println("--------使用for的iterator-------");
// 利用for循环遍历
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
String str = it.next();
if(str.equals("String 2")) {
it.remove();
System.out.println(str + "(当字符串为“String 2”时从迭代器中删除它)");
}else {
System.out.println(str);
}
}
System.out.println("删除“String 2”后的list为:" + list);
}
}
出力結果:
イテレーターを使用して、リスト内の要素を削除できます。
(4)、LinkedList
シーケンスは次のように分けられます。ファーストインファーストアウトFIFO、ファーストインラストアウトFILO
FIFOは
Javaではキューとも呼ば れ、FILOはJavaではスタックとも呼ばれます。
[1] ArrayListと同様に、LinkedListもListインターフェイスを実装しています。
[2]しかし同時に、LinkedListはListインターフェースを実装するだけでなく、二重にリンクされたリスト構造Dequeも実装します。これにより、最初と最後にデータを簡単に挿入および削除できます。
import java.util.LinkedList;
import java.util.List;
public class CollectionsTest {
public static void main(String[] args) {
//LinkedList是一个双向链表结构的list
LinkedList<String> list =new LinkedList<>();
//所以可以很方便的在头部和尾部插入数据
//在最后插入新的字符串
list.addLast("String 1");
list.addLast("String 2");
list.addLast("String 3");
System.out.println("加入数据后的list:\t\t" + list);
//在最前面插入新的字符串
list.addFirst("String X");
System.out.println("最前面插入新的字符串后的list:\t" + list);
//查看最前面的字符串
System.out.println("最前面的字符串:\t\t " + list.getFirst());
//查看最后面的字符串
System.out.println("最后面的字符串:\t\t " + list.getLast());
//查看不会导致元素被删除
System.out.println("再次查看list:\t\t" + list);
//取出最前面的字符串
System.out.println("取出来的最前面的字符串:\t " + list.removeFirst());
//取出最后面的字符串
System.out.println("取出来的最后面的字符串:\t " + list.removeLast());
//取出会导致元素被删除
System.out.println("再次查看list:\t\t" + list);
}
}
出力結果:
[3] ListとDequeの実装に加えて、LinkedListはQueueインターフェイス(queue)も実装します。
キューは、ファーストインファーストアウトのキューFIFOであり、一般的に使用される方法は次のとおりです。
offer 在最后添加元素
poll 取出第一个元素
peek 查看第一个元素
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class CollectionsTest {
public static void main(String[] args) {
// 和ArrayList一样,LinkedList也实现了List接口
List<String> list = new LinkedList<>();
// 同时,LinkedList还实现了Deque接口
// Deque代表双向链表
Deque<String> deque = new LinkedList<>();
// 同时,LinkedList也实现了Queue这个接口
// Queue代表FIFO 先进先出的队列
Queue<String> queue = new LinkedList<>();
for (int i = 0; i < 2; i++) {
queue.offer("String " + i); // 在最后添加元素
}
for (int i = 0; i < 3; i++) {
queue.add("String " + (i + 2)); // 与offer()同样的效果,添加元素
} //
System.out.println("利用offer()方法或add()方法添加元素。");
System.out.println("添加元素后的queue为:\t" + queue);
System.out.println();
String first = queue.peek(); //
System.out.println("利用peek()方法查看第一个元素。");
System.out.println("查看第一个元素:\t\t " + first);
System.out.println();
queue.poll(); //
System.out.println("利用poll()方法取出第一个元素。");
System.out.println("取出第一个元素......");
System.out.println("第一个元素取出后的queue为:\t" + queue);
}
}
出力結果:
(5)バイナリツリー
バイナリツリーはさまざまなノードで構成されています。バイナリツリーの
特徴:
各ノードは、左の子ノード、右の子ノード、および値を持つことができます。
public class Node {
// 左子节点
public Node leftNode;
// 右子节点
public Node rightNode;
// 值
public Object value;
}
[1]、バイナリツリー-データの挿入-原則:
次の10個のランダムな数字がバイナリツリー
67、7、30、73、10、0、78、81、10、74
でソートされているとします。ソートの最初のステップは、データをバイナリツリーに
挿入することです。挿入の基本的なロジックは小さいです。 、左側の同じ場所が、右側の大きい
ルートノード1. 67
2. 7が67未満である、67の左ノードである
3. 30が67未満である、67の左ノードが発見され、そして30は、7以上であります
7の右側のノードを配置します。4。73は67より大きい、67の右側のノードを配置します。5。10は
67より小さい、67の左側のノードを見つける、10は7より大きい、7の右側のノードを見つける、30、10は30より小さい30の左側のノードに配置します。
...
...
9.10 67未満で左ノードを見つける677,10 7より大きい7、30の右ノード7を見つける、10 30時間以上で、左ノード30を10、10、10と同じ大きさで見つける左側に置く
public class Node {
/**
* 左子节点
*/
public Node leftNode;
/**
* 右子节点
*/
public Node rightNode;
/**
* 值
*/
public Object value;
/**
* 插入数据
* @param v
*/
public void add(Object v) {
// 如果当前节点没有值,就把数据放在当前节点上
if (value == null) {
value = v;
} else { // 如果当前节点有值,就进行判断,新增的值与当前的值的大小关系
// 新增的值,比当前值小或者相同
if ((Integer) value - (Integer) v >= 0) {
if (leftNode == null) {
leftNode = new Node();
}
leftNode.add(v);
}
// 新增的值,比当前值大
else {
if (rightNode == null) {
rightNode = new Node();
}
rightNode.add(v);
}
}
}
public static void main(String[] args) {
int randoms[] = new int[] { 67, 7, 30, 73, 10, 0, 78, 81, 10, 74 };
Node roots = new Node();
for (int number : randoms) {
roots.add(number);
}
}
}
[2]、バイナリツリートラバーサル:
前のステップの挿入動作により、データは実際にソートされます。次に行うことは、これらのソートされたデータを確認し、一般的に使用されるリストまたは配列形式にトラバースすることです。バイナリツリートラバーサルは、1次、ミドルオーダー、および2次[1次]に分けられます。つまり、トラバース後のミドルナンバーです。左側に配置[ミドルオーダー]:ミドルナンバーをトラバースして中央に配置[ポストオーダー]:ミドルナンバーをトラバースして右側に配置
import java.util.ArrayList;
import java.util.List;
public class Node {
/**
* 左子节点
*/
public Node leftNode;
/**
* 右子节点
*/
public Node rightNode;
/**
* 值
*/
public Object value;
/**
* 插入数据
* @param v
*/
public void add(Object v) {
// 如果当前节点没有值,就把数据放在当前节点上
if (value == null) {
value = v;
} else { // 如果当前节点有值,就进行判断,新增的值与当前的值的大小关系
// 新增的值,比当前值小或者相同
if ((Integer) value - (Integer) v >= 0) {
if (leftNode == null) {
leftNode = new Node();
}
leftNode.add(v);
}
// 新增的值,比当前值大
else {
if (rightNode == null) {
rightNode = new Node();
}
rightNode.add(v);
}
}
}
// 先序遍历所有的节点
public List<Object> valuesOfLeft() {
List<Object> values = new ArrayList<>();
// 当前节点
values.add(value);
// 左节点的遍历结果
if (leftNode != null) {
values.addAll(leftNode.valuesOfLeft());
}
// 右节点的遍历结果
if (rightNode != null) {
values.addAll(rightNode.valuesOfLeft());
}
return values;
}
// 中序遍历所有的节点
public List<Object> valuesOfCenter() {
List<Object> values = new ArrayList<>();
// 左节点的遍历结果
if (leftNode != null) {
values.addAll(leftNode.valuesOfCenter());
}
// 当前节点
values.add(value);
// 右节点的遍历结果
if (rightNode != null) {
values.addAll(rightNode.valuesOfCenter());
}
return values;
}
// 后序遍历所有的节点
public List<Object> valuesOfRight() {
List<Object> values = new ArrayList<>();
// 左节点的遍历结果
if (leftNode != null) {
values.addAll(leftNode.valuesOfRight());
}
// 右节点的遍历结果
if (rightNode != null) {
values.addAll(rightNode.valuesOfRight());
}
// 当前节点
values.add(value);
return values;
}
public static void main(String[] args) {
int randoms[] = new int[] { 67, 7, 30, 73, 10, 0, 78, 81, 10, 74 };
Node roots = new Node();
for (int number : randoms) {
roots.add(number);
}
System.out.println("先序遍历的二叉树:\t" + roots.valuesOfLeft());
System.out.println("中序遍历的二叉树:\t" + roots.valuesOfCenter());
System.out.println("后序遍历的二叉树:\t" + roots.valuesOfRight());
}
}
出力結果:
(6)、HashMap
HashMapがデータを保存する方法は-キーと値のペア
HashMapの場合、キーは一意であり、繰り返すことはできません。
したがって、同じキーを使用してマップに異なる値を挿入すると、古い要素が上書きされ、最後に挿入された要素のみが残ります。
ただし、対応するキーが異なる限り、同じオブジェクトを値としてマップに挿入できます。
(7)、ハッシュセット
セット内の要素を繰り返すことはできません
[1]、注文なし
セット内の要素には順序がありません。
厳密に言えば、
HashSetの特定の順序は、要素の挿入の順序ではなく、挿入の順序でもハッシュコードの順序でもありません。
つまり、HashSetに0〜9を挿入することも同じであり、JVMのバージョンによって表示の順序が異なります。
import java.util.HashSet;
public class HashSetTest {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("hello");
set.add("world");
set.add("!!!");
System.out.println(set);
}
}
動作結果:
[2]、トラバース
セットには、指定された位置にある要素を取得するためにget()は提供されません
ので、イテレータや拡張forループ横断のために必要とされます
[3] HashSetとHashMapの関係
HashSetのソースコードを観察すると、HashSet自体には独立した実装はありませんが、Mapがカプセル化されている
ことがわかります
。HashSetはMapのキーとして存在し
、値はPRESENTという名前の静的オブジェクトオブジェクトです。クラス属性なので、1つだけになります。
private static final Object PRESENT = new Object();
package collection;
import java.util.AbstractSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable {
// HashSet里封装了一个HashMap
private HashMap<E, Object> map;
private static final Object PRESENT = new Object();
// HashSet的构造方法初始化这个HashMap
public HashSet() {
map = new HashMap<E, Object>();
}
// 向HashSet中增加元素,其实就是把该元素作为key,增加到Map中
// value是PRESENT,静态,final的对象,所有的HashSet都使用这么同一个对象
public boolean add(E e) {
return map.put(e, PRESENT) == null;
}
// HashSet的size就是map的size
public int size() {
return map.size();
}
// 清空Set就是清空Map
public void clear() {
map.clear();
}
// 迭代Set,就是把Map的键拿出来迭代
public Iterator<E> iterator() {
return map.keySet().iterator();
}
}
(8)、セット
HashSet
UnorderedLinkedHashSet挿入順序
TreeSetに従って小さいものから大きいものへと並べ替えます
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;
public class HashSetTest {
public static void main(String[] args) {
HashSet<String> numberSet1 =new HashSet<>();
//HashSet中的数据不是按照插入顺序存放
numberSet1.add("bbbbb");
numberSet1.add("aaaaa");
numberSet1.add("ccccc");
System.out.println(numberSet1);
LinkedHashSet<String> numberSet2 =new LinkedHashSet<>();
//LinkedHashSet中的数据是按照插入顺序存放
numberSet2.add("bbbbb");
numberSet2.add("aaaaa");
numberSet2.add("ccccc");
System.out.println(numberSet2);
TreeSet<String> numberSet3 =new TreeSet<>();
//TreeSet 中的数据是进行了排序的
numberSet3.add("bbbbb");
numberSet3.add("aaaaa");
numberSet3.add("ccccc");
System.out.println(numberSet3);
}
}
動作結果:
(9)ArrayListとLinkedListの違い
ArrayListは、[シーケンスリスト]、[データの高速検索]、[データの挿入と削除が遅い
]の構造です。LinkedListは[リンクリスト]構造、[データの検索が遅い]、[データの挿入と削除が高速]です。
(10)ArrayListとHashSetの違い
ArrayListには順序があり、データを繰り返すことができます
。HashSetには順序がなく、データを繰り返すことはできません。
HashSetの繰り返しの判断基準は、
最初にハッシュコードが同じかどうかを確認します。
ハッシュコードが異なる場合は、異なるデータと見なされます。
ハッシュコードが同じ場合は、等しいと比較します。等しい場合は同じデータです。それ以外の場合は、異なるデータです。
(11)HashMapとHashTableの違い
HashMapとHashtableはどちらもMapインターフェイスを実装しており、どちらもデータを格納するためのキーと値のペアです。
違い1:
HashMapはnull値を
格納でき、Hashtableはnull値を格納できません。
違い2:
HashMapはスレッドセーフクラスではありません
Hashtableはスレッドセーフクラスです
import java.util.HashMap;
import java.util.Hashtable;
public class MapTableTest {
public static void main(String[] args) {
// HashMap 可以存放 null 值
HashMap<String, Integer> map = new HashMap<>();
map.put("first", null);
System.out.println(map);
// Hashtable 不能存放null 值
Hashtable<String, Integer> table = new Hashtable<>();
table.put("first", null);
System.out.println(table);
}
}
動作結果:
(12)HashCodeの原則
[1]、効率の比較
HashCodeによってもたらされる違いは次のとおりです。
[2]、HashCodeの原則
たとえば、英語-中国語辞書で単語の対応する中国語の意味を検索する場合、その単語がLengthdaryであると仮定し、最初に555ページのディレクトリでLengthdaryを検索します。
次に、555ページを参照してください。このページには単語が1つしかないだけでなく、量も非常に少ないので、1つずつ比較して、目的の単語の長さをすばやく見つけます。
555は、Lengendaryに対応するハッシュコードに相当します
[3]、HashMapの優れたパフォーマンスの理由を分析します
-----
ハッシュコードの概念-----すべてのオブジェクトには対応するハッシュコード(ハッシュ値)があります。たとえば
、文字列「gareen」は1001に対応します(実際にはそうではありません。ここでは、仮想値を理解するのに便利な値です)。 )たとえば
、文字列「temoo」は1004に対応します。
たとえば、文字列「db」は1008に対応します。
たとえば、文字列「annie」は1008に対応し
ます。-----データを保存します-----
長さが次の配列を準備します。2000、特別なハッシュコードアルゴリズムを設定して、すべての文字列に対応するハッシュコード
が0〜999になるようにします。名前が「gareen」のヒーローを保存するには、ヒーローと名前がキーと値のペアを形成し、
名前が「temoo」のヒーローを配列の位置1001に格納するには、ヒーローを配列の位置1004に格納します。
名前が「db」のヒーローを格納するには、ヒーローを配列の位置1008に格納します。
名前が「annie」であるが、「annie」のハッシュコード1008に対応する場所にすでにdbヒーローが存在するヒーローを保存するには、ここにリンクリストを作成し、dbヒーローの後にannieを保存し
ます-----データの検索- ---たとえば
、gareenを見つけるには、最初に「gareen」のハッシュコードを1001と計算し、1001の添え字に従って配列内で見つけます(配列の添え字に従って見つけるのは非常に高速です)。1001の位置は1つだけであることがわかります。ヒーロー、そしてヒーローはガリーンです。
たとえば、アニーを見つけるには、最初に「アニー」のハッシュコードを1008と計算します。1008の添え字に従って、配列内を見つけて、位置1008に2人のヒーローがいることを確認し、2人のヒーローの名前を1つずつ比較します(等しい) 、現時点で比較する量がはるかに少なく、ターゲットヒーローをすぐに見つけることができるためです。
これはクエリにハッシュマップを使用するため、非常に高速です。
これは時間に空間を使う考え方です
[4]、HashSetはそれが繰り返されるかどうかを判断します
HashSetのデータを繰り返すことはできません。同じデータを一緒に保存することはできません。繰り返されるかどうかを判断するにはどうすればよいですか。
HashSetとHashMapの関係によると、HashSetには独自の実装がなく、代わりにHashMapをカプセル化するため、基本的にHashMapのキーが重複しているかどうかを判断することがわかりました。
前の学習ステップでは、キーが繰り返される
かどうかは、ハッシュコードが同じかどうか、ハッシュ
コードが異なる
場合は別のピットにあるか、ハッシュコードが同じ場合は同じピットにあるかという2つのステップで判断されます。また、
等しいものを比較する必要があります。等しいものが同じである場合、それは重複データです。
等しいものが同じでない場合、それは異なるデータです。
(13)コンパレータ
Collections.sort()メソッドは、コレクション内の要素を並べ替えることができます
ただし、コレクションがある場合、その中の要素はすべてPersonタイプのオブジェクトであり、その属性には、並べ替える属性に応じて、名前、年齢、住所などが含まれます。
これにはコンパレータが必要です!!!
[1]、方法1
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
public class ComparatorTest {
public static void main(String[] args) {
Random r = new Random();
List<Person> persons = new ArrayList<>();
for (int i = 0; i < 10; i++) {
persons.add(new Person("张" + i, r.nextInt(20) + 20, "西安"));
}
System.out.println("初始化后的集合:");
System.out.println(persons);
// 直接调用sort会出现编译错误,因为Person有各种属性
// 到底按照哪种属性进行比较,Collections也不知道,不确定,所以没法排
// Collections.sort(persons);
// 引入Comparator,指定比较的算法
Comparator<Person> c = new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
// 按照hage进行排序
if (p1.age >= p2.age)
return 1; // 正数表示p1比hp2要大
else
return -1;
}
};
Collections.sort(persons, c);
System.out.println("按照年龄排序后的集合:");
System.out.println(persons);
}
}
class Person {
public String name;
public int age;
public String address;
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "\n" + "Name : " + name + "\tAge : " + age + "\tAddress : " + address;
}
}
結果:
[2]、方法2
PersonクラスにComparableインターフェイスを実装させます
。クラスに比較アルゴリズム
Collections.sortを提供して、ソートに十分な情報を取得します。追加のコンパレータComparatorを提供する必要はありません。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
public class ComparatorTest {
public static void main(String[] args) {
Random r = new Random();
List<Person> persons = new ArrayList<>();
for (int i = 0; i < 10; i++) {
persons.add(new Person("张" + i, r.nextInt(20) + 20, "西安"));
}
System.out.println("初始化后的集合:");
System.out.println(persons);
// 直接调用sort会出现编译错误,因为Person有各种属性
// 到底按照哪种属性进行比较,Collections也不知道,不确定,所以没法排
// Collections.sort(persons);
// 引入Comparator,指定比较的算法
Comparator<Person> c = new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
// 按照hage进行排序
if (p1.age >= p2.age)
return 1; // 正数表示p1比hp2要大
else
return -1;
}
};
//Person类实现了接口Comparable,即自带比较信息。
//Collections直接进行排序,无需额外的Comparator
Collections.sort(persons);
System.out.println("按照年龄排序后的集合:");
System.out.println(persons);
}
}
class Person implements Comparable<Person> {
public String name;
public int age;
public String address;
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "\n" + "Name : " + name + "\tAge : " + age + "\tAddress : " + address;
}
@Override
public int compareTo(Person o) {
if (age > o.age) {
return 1;
}
return -1;
}
}
動作結果: