Java中Collection和Map體系(Java容器)
Java常用容器類繼承關係圖解
Java容器類簡介
Java中容器類主要分爲四中體系:List
、Set
、Queue
、Map
。
List
:代表有序、可重複的集合;
Set
:代表無序、不可重複的集合;
Queue
:代表一種隊列集合實現;
Map
:代表具有映射關係的集合。
容器簡介
Collection
:Collection體系中的基本接口,沒有直接實現類,只有子接口(包含List
、Set
、Queue
接口等);定義了對集合的基本操作方法(增、刪、改、查、遍歷、獲取集合大小等);
List
:是一個有序的 Collection,使用此接口能夠精確的控制每個元素插入的位置,能夠通過索引(元素在List中位置,類似於數組的下標)來訪問List中的元素,第一個元素的索引爲 0,而且允許有相同的元素;List 接口存儲一組不唯一,有序(插入順序)的對象。
Set
:Set集合與Collection集合基本相同,沒有提供其他額外的方法。實際上Set就是Collection,只是行爲略有不同;Set 接口存儲一組唯一,無序的對象。
Queue
:Queue表示隊列這種數據結構,隊列通常是指**“先進先出”(FIFO,first-in-first-out)的容器**。新元素插入(offer)到隊列的尾部,訪問元素(poll)操作會返回隊列頭部的元素。通常,隊列不允許隨機訪問隊列中的元素。
Stack
:Stack表示棧結構。與隊列(Queue)不同,它實現了一個標準的**“後進先出”(LIFO,last-in-first-out)的容器**。
Map
:Map 接口存儲一組鍵值對對象,提供key(鍵)到value(值)的映射。key和value之間存在單向一對一關係,即通過指定的key,總能找到唯一的、確定的value。從Map中取出數據時,只要給出指定的key,就可以取出對應的value,Map的key不能重複,如果重複添加,會覆蓋原來的值。
容器的元素存取與遍歷
List
存取元素
List<String> list = new ArrayList<>();
// 增加元素
list.add("BB");
list.add("CC");
list.add(0, "AA");
// 根據索引獲取值
String value = list.get(0);
遍歷
List<String> list = new ArrayList<>();
// 增加元素
list.add("AA");
list.add("BB");
list.add("CC");
// 方式1:使用foreach遍歷List,推薦
System.out.println("使用foreach遍歷List:");
for (String str : list) { //也可以改寫for(int i=0;i<list.size();i++)這種形式
System.out.println(str);
}
// 方式2:變爲數組相關的內容進行遍歷
System.out.println("變爲數組相關的內容進行遍歷:");
String[] strArray = new String[list.size()];
list.toArray(strArray);
for (int i = 0; i < strArray.length; i++) { //這裏也可以改寫爲 foreach(String str:strArray)這種形式
System.out.println(strArray[i]);
}
// 方式3:使用迭代器進行相關遍歷
System.out.println("使用迭代器進行相關遍歷:");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) { //判斷下一個元素之後有值
System.out.println(iterator.next());
}
Set
存取元素
Set<String> set = new HashSet<>();
// 添加元素
set.add("AA");
set.add("BB");
set.add("CC");
// set 集合沒有提供快速取出指定位置或者指定元素的方法
// 只能遍歷取值,或者將set集合轉換爲list集合,在使用list集合的方法取數據
遍歷
Set<String> set = new HashSet<>();
// 添加元素
set.add("AA");
set.add("BB");
set.add("CC");
// 方式1:使用foreach遍歷set,推薦
System.out.println("使用foreach遍歷Set:");
for (String str : set) { // 沒有 for(int i=0;i<list.size();i++)這種形式
System.out.println(str);
}
// 方式2:變爲數組相關的內容進行遍歷
System.out.println("把鏈表變爲數組相關的內容進行遍歷:");
String[] strArray = new String[set.size()];
set.toArray(strArray);
for (int i = 0; i < strArray.length; i++) { //這裏也可以改寫爲 foreach(String str:strArray)這種形式
System.out.println(strArray[i]);
}
// 方式3:使用迭代器進行相關遍歷
System.out.println("使用迭代器進行相關遍歷:");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) { //判斷下一個元素之後有值
System.out.println(iterator.next());
}
// 方式4:變爲List集合後使用操作
List<String> list = new ArrayList<>(set);
System.out.println(list);
Map
存取元素
Map<Integer,String> map = new HashMap<>();
// 添加元素
map.put(1,"AA");
map.put(2,"BB");
map.put(3,"CC");
// 根據 key 獲取 value
String value = map.get(1);
遍歷
Map<Integer, String> map = new HashMap<>();
// 添加元素
map.put(1, "AA");
map.put(2, "BB");
map.put(3, "CC");
// 方式1
System.out.println("通過Map.keySet遍歷key和value:");
for (Integer integer : map.keySet()) {
String value = map.get(integer);
System.out.println("key=" + integer + "; value=" + value);
}
// 方式2,推薦
System.out.println("通過Map.entrySet遍歷key和value:");
for (Map.Entry<Integer, String> integerStringEntry : map.entrySet()) {
System.out.println("key=" + integerStringEntry.getKey() + "; value=" + integerStringEntry.getValue());
}
// 方式3
System.out.println("通過Map.entrySet使用iterator遍歷key和value:");
Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, String> entry = iterator.next();
System.out.println("key=" + entry.getKey() + "; value=" + entry.getValue());
}
// 方式4,只能遍歷value
System.out.println("通過Map.values()遍歷所有的value,但不能遍歷key:");
for (String value : map.values()) {
System.out.println("value=" + value);
}
Queue
存取元素
Queue<String> queue = new ArrayDeque<>();
queue.add("AA");
queue.offer("BB");
queue.offer("CC");
queue.offer("DD");
System.out.println(queue);
String element = queue.element(); // 獲取,但是不移除此隊列的頭
String peek = queue.peek(); // 獲取但不移除此隊列的頭;如果此隊列爲空,則返回 null
String remove = queue.remove(); // 獲取並移除此隊列的頭
String poll = queue.poll(); // 獲取並移除此隊列的頭,如果此隊列爲空,則返回 null
System.out.println("element: " + element + " peek: " + peek + " remove: " + remove + " poll: " + poll);
System.out.println(queue);
遍歷元素
Queue<String> queue = new ArrayDeque<>();
queue.add("AA");
queue.offer("BB");
queue.offer("CC");
queue.offer("DD");
// 方式1:集合方式遍歷,元素不會被移除
System.out.println("集合方式遍歷,元素不會被移除: ");
for (String string : queue) {
System.out.println(string);
}
// 方式2:隊列方式遍歷,元素逐個被移除
System.out.println("隊列方式遍歷,元素逐個被移除: ");
while (queue.peek() != null) {
System.out.println(queue.poll());
}
Stack
存取元素
Stack<String> stringStack = new StringStack();
stringStack.add("AA");
stringStack.push("BB");
stringStack.push("CC");
stringStack.push("DD");
System.out.println(stringStack);
String peek = stringStack.peek(); // 查看堆棧頂部的對象,但不從堆棧中移除它
String pop = stringStack.pop(); // 移除堆棧頂部的對象,並作爲此函數的值返回該對象
System.out.println("peek: " + peek + " pop: " + pop);
遍歷
Stack<String> stringStack = new StringStack();
stringStack.add("AA");
stringStack.push("BB");
stringStack.push("CC");
stringStack.push("DD");
// 方式1:集合遍歷方式
System.out.println("集合遍歷方式: ");
for (String string : stringStack) {
System.out.println(string);
}
// 方式2:棧彈出遍歷方式
System.out.println("棧彈出遍歷方式: ");
while (!stringStack.empty()){
System.out.println(stringStack.pop());
}
線程安全/線程不安全集合
Java 中常見的線程安全的集合:
Vector:就比ArrayList多了個同步化機制(線程安全),因爲效率較低,現在已經不太建議使用
Statck:棧,繼承Vector,先進後出(LIFO)
Hashtable:就比HashMap多了個線程安全
將線程不安全的集合變爲線程安全的集合:
使用 Collections.synchronizedXxx()
方法
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
Map<Integer,String> map = new HashMap<>();
// 將線程不安全集合變爲線程安全集合
List<String> synchronizedList = Collections.synchronizedList(list);
Set<String> synchronizedSet = Collections.synchronizedSet(set);
Map<Integer, String> synchronizedMap = Collections.synchronizedMap(map);
// ...
使用比較器對元素自定義排序
Comparable和Comparator接口都是爲了對類進行比較,衆所周知,諸如Integer,double等基本數據類型,java可以對他們進行比較,而對於類的比較,需要人工定義比較用到的字段比較邏輯。可以把Comparable理解爲內部比較器,而Comparator是外部比較器,基本的寫法如下:
使用 Comparable
排序
// 定義實體類
public class UserA implements Comparable<UserA> {
public String name;
public int age;
public UserA(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(UserA o) {
return this.age - o.age;
}
@Override
public String toString() {
return "UserA{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
// 使用排序
public void comparableTest(){
List<UserA> list = new ArrayList<>();
list.add(new UserA("張三",23));
list.add(new UserA("李四",22));
list.add(new UserA("王五",25));
System.out.println("before: " + list);
Collections.sort(list);
System.out.println("after: " + list);
}
使用 Comparator
排序
// 定義實體類
public class UserB{
public String name;
public int age;
public UserB(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "UserA{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
// 使用排序
public void comparatorTest(){
List<UserB> list = new ArrayList<>();
list.add(new UserB("張三",23));
list.add(new UserB("李四",22));
list.add(new UserB("王五",25));
System.out.println("before: " + list);
Collections.sort(list, new Comparator<UserB>() {
@Override
public int compare(UserB o1, UserB o2) {
return o1.age - o2.age;
}
});
System.out.println("after: " + list);
}
Java集合和數組的區別
- 數組的長度在初始化時就已經固定,也就是說只能保存固定長度的數據。而集合的長度是可以動態變化的,所以可以保存數量不確定的數據。
- 數組元素即可以是基本類型,也可以是對象。集合裏只能保存對象(實際上只是保存對象的引用變量),基本數據類型的值要轉換成對應的包裝類才能放入集合類中。