Java中Map和Set的常見用法舉例
一、Map和Set的概念
Map和set是一種專門用來進行搜索的容器或者數(shù)據結構,其搜索的效率與其具體的實例化子類有關,常見的搜索方式如下:
1.直接遍歷,時間復雜度為O(N),元素如果比較多效率會非常慢
2.二分查找,時間復雜度為O(logN) ,但搜索前必須要求序列是有序的
上述排序比較適合靜態(tài)類型的查找,即一般不會對區(qū)間進行插入和刪除操作了,而現(xiàn)實中的查找比如:
1.根據姓名查詢考試成績
2.通訊錄,即根據姓名查詢聯(lián)系方式
3.不重復集合,即需要先搜索關鍵字是否已經在集合中
可能在查找時進行一些插入和刪除的操作,即動態(tài)查找,那上述兩種方式就不太適合了,下面介紹的Map和Set是一種適合動態(tài)查找的集合容器
二、模型
一般把搜索的數(shù)據稱為關鍵字(Key),和關鍵字對應的稱為值(Value),將其稱為Key-Value的鍵值對,所以模型有兩種:
1.純Key模型
有一個英文詞典,快速查找一個單詞是否在詞典中
快速查找某個名字在不在通訊錄中
2.Key-Value 模型
統(tǒng)計文件中每個單詞出現(xiàn)的次數(shù),統(tǒng)計結果是每個單詞都有與其對應的次數(shù):<單詞,單詞出現(xiàn)的次數(shù)>
梁山好漢的江湖綽號:每個好漢都有自己的江湖綽號
而Map中存儲的就是key-value的鍵值對,Set中只存儲了Key

三、Map的說明
Map是一個接口類,該類沒有繼承自Collection,該類中存儲的是<K,V>結構的鍵值對,并且K一定是唯一的,不能重復
3.1 Map.Entry<K, V>的說明
Map.Entry<K, V> 是Map內部實現(xiàn)的用來存放<key, value>鍵值對映射關系的內部類,該內部類中主要提供了<key, value>的獲取,value的設置以及Key的比較方式
| 方法 | 解釋 |
|---|---|
| K getKey() | 返回entry中的key |
| V getValue() | 返回entry中的value |
| V setValue(V value) | 將鍵值對中的value替換為指定value |
注意:Map.Entry<K,V>并沒有提供設置Key的方法
3.2 Map 的常用方法
| 方法 | 解釋 |
|---|---|
| V get(Object key) | 返回key對應的value |
| V getOrDefault(Object key, V defaultValue) | 返回key對應的value,key不存在,返回默認值 |
| V put(K key, V value) | 設置key對應的value |
| V remove(Object key) | 刪除key對應的映射關系 |
| Set< K > keySet() | 返回所有key的不重復集合 |
| Collection< V > values() | 返回所有value的可重復集合 |
| Set<Map.Entry<K,V>>entrySet() | 返回所有的key-value映射關系 |
| boolean containsKey(Object key) | 判斷是否包含key |
| boolean containsValue(Object Value) | 判斷是否包含value |
注意:
1.Map是一個接口,不能直接實例化對象,如果要實例化對象只能實例化其實現(xiàn)類TreeMap或者HashMap
2.Map中存放鍵值對的Key是唯一的,value是可以重復的
3.在TreeMap中插入鍵值對時,key不能為空,否則就會拋NullPointerException異常,value可以為空。但是HashMap的key和value都可以為空
4.Map中的Key可以全部分離出來,存儲到Set中來進行訪問(因為Key不能重復)
5.Map中的value可以全部分離出來,存儲在Collection的任何一個子集合中(value可能有重復)
6.Map中鍵值對的Key不能直接修改,value可以修改,如果要修改key,只能先將該key刪除掉,然后再來進行重新插入
四、Set的說明
Set與Map主要的不同有兩點:Set是繼承自Collection的接口類,Set中只存儲了Key
4.1 Set的常用方法
| 方法 | 解釋 |
|---|---|
| boolean add(E e) | 添加元素,但重復元素不會被添加成功 |
| void clear() | 清空集合 |
| boolean contains(Object o) | 判斷o是否在集合中 |
| lterator< E > iterator() | 返回迭代器 |
| boolean remove(Object o) | 刪除集合中的o |
| int size() | 返回set中元素的個數(shù) |
| boolean isEmpty() | 檢測set是否為空,空返回true,否則返回false |
| Object[] toArray() | 將set中的元素轉換為數(shù)組返回 |
| boolean containsAll(Collection<?>c) | 集合c中的元素是否在set中全部存在,是返回true,否則返回false |
| boolean addAll(Conllection<? extends E>c) | 將集合c中的元素添加到set中,可以達到去重的效果 |
注意:
1.Set是繼承自Collection的一個接口類
2.Set中只存儲了key,并且要求key一定要唯一
3.TreeSet的底層是使用Map來實現(xiàn)的,其使用key與Object的一個默認對象作為鍵值對插入到Map中的
4.Set最大的功能就是對集合中的元素進行去重
5.實現(xiàn)Set接口的常用類有TreeSet和HashSet,還有一個LinkedHashSet,LinkedHashSet是在HashSet的基礎上維護了一個雙向鏈表來記錄元素的插入次序
6.Set中的Key不能修改,如果要修改,先將原來的刪除掉,然后再重新插入
7.TreeSet中不能插入null的key,HashSet可以
附:一些有關 Map 和 Set 的題目
1. 統(tǒng)計一組數(shù)據中的每個數(shù)據出現(xiàn)了多少次
/**
* 統(tǒng)計一組數(shù)據中的每個數(shù)據出現(xiàn)了多少次
*/
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
char[] arr = {'a','c','e','c','b','d','f','c','d'};
Map<Character,Integer> map = computer(arr);
System.out.println(map);
}
public static Map<Character,Integer> computer(char[] arr){
Map<Character,Integer> map = new HashMap<>();
//如果在 map 中沒有數(shù)組對應的 Key,那么就添加 1個 Key 進去
//如果在 map 中包含了和數(shù)組對應的 Key,且 Key 對應 count個 Val,
//那么就添加 count + 1個 Key 進去
for (char x :arr) {
if(map.containsKey(x)){
int count = map.get(x);
map.put(x, count+1);
}else {
map.put(x,1);
}
}
return map;
}
}
2. 將一組數(shù)據中多余的數(shù)據去重
/**
* 將一組數(shù)據中多余的數(shù)據去重
*/
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
int[] arr = {1,3,5,7,9,3,2,4,6,8,5,4,6};
Set<Integer> set = new HashSet<>();
for (int x : arr) {
set.add(x);
}
System.out.println(set);
}
}
3. 找出第一個重復出現(xiàn)的數(shù)據
/**
* 在一組數(shù)據中,找出第一個重復出現(xiàn)的數(shù)據
*/
import java.util.HashSet;
import java.util.Set;
public class Test6 {
public static void main(String[] args) {
int[] arr = {1,3,5,7,9,3,2,4,6,8,5,4,6};
Set<Integer> set = new HashSet<>();
int i = 0;
//如果在添加元素到 set 之前,就已經發(fā)現(xiàn)了 set 中包含某個數(shù)組的元素,
//那么就 break
for (i = 0; i < arr.length; i++) {
if(set.contains(arr[i])){
break;
}
set.add(arr[i]);
}
System.out.println(arr[i]); //拿到的就是第一次出現(xiàn)的重復值
}
}
4. 只出現(xiàn)一次的數(shù)字
方法一:
/**
* 利用兩兩元素之間異或,最終得出的就是單獨元素
*/
class Solution {
public int singleNumber(int[] nums) {
int x = nums[0];
for(int i= 1; i<nums.length; i++){
x = x^nums[i];
}
return x;
}
}
方法二:
/**
* 利用集合 Set
* 如果集合中有相同元素,就移除,反之,就添加
*/
class Solution {
public int singleNumber(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int i=0; i<nums.length; i++){
if(!set.contains(nums[i])){
set.add(nums[i]);
}else{
set.remove(nums[i]);
}
}
for(int i=0; i<nums.length; i++){
if(set.contains(nums[i])){
return nums[i];
}
}
return -1;
}
}
5. 復制帶隨機指針的鏈表
本題需要詳細掌握 Map 的操作,與其對應的映射關系,才能將本題理解到位。
/**
* 通過 Map 的映射關系
* 知道了 Key 的狀態(tài),實現(xiàn) Value 的狀態(tài)
* 即通過舊節(jié)點之間的關系,實現(xiàn)新節(jié)點之間的關系
*/
class Solution {
public Node copyRandomList(Node head) {
if(head == null) return null;
//Key - 舊節(jié)點,Value - 新節(jié)點
Map<Node,Node> map = new HashMap<>();
Node cur = head;
//1. 創(chuàng)建新的節(jié)點
while(cur != null){
map.put(cur, new Node(cur.val));
cur = cur.next;
}
cur = head;
//2. 連接各個節(jié)點
while(cur != null){
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
return map.get(head);
}
}
6. 寶石與石頭
方法一
/**
* 方法一
* 1. 將 jewels 字符串中的字符放入集合 set 中
* 2. 查看集合 set 中是否包含 stones 字符數(shù)組中字符
* 3. 用 count 計數(shù),最后返回
*/
class Solution {
public int numJewelsInStones(String jewels, String stones) {
Set<Character> set = new HashSet<>();
for(char x:jewels.toCharArray()){ //1.
set.add(x);
}
int count = 0;
for(char j:stones.toCharArray()){ //2.
if(set.contains(j)){
count++;
}
}
return count; //3.
}
}
方法二
/**
* 方法二
* 兩層 for 循環(huán)
*/
class Solution {
public int numJewelsInStones(String jewels, String stones) {
int count = 0;
for(int i=0; i<jewels.length(); i++){
for(int j=0; j<stones.length();j++){
if( jewels.charAt(i) == stones.charAt(j) ){
count++;
}
}
}
return count;
}
}
7. 鍵盤壞了的鍵
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
/**
* 1. 將實際輸入的文字 str2 放在集合 set 中
* 2. for 循環(huán)遍歷應該輸入的文字 str1,
* 若集合 set 不包含某個字符,且線性表之前也未出現(xiàn)該字符,就輸出打印
* 3. 將步驟 2 中不包含的字符放入順序表中,以便下一次判斷
*/
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String str1 = scanner.nextLine(); //應該輸入的文字
String str2 = scanner.nextLine(); //實際輸入的文字
out(str1,str2);
}
public static void out(String str1, String str2){
Set<Character> set = new HashSet<>();
ArrayList<Character> list = new ArrayList<>();
for (char x:str2.toUpperCase().toCharArray()) {
set.add(x); //1.
}
for(char x:str1.toUpperCase().toCharArray()){ //2.
if(!set.contains(x) && !list.contains(x)){
System.out.print(x);
}
list.add(x); //3.
}
}
}
8. 前K個高頻單詞
class Solution {
public List<String> topKFrequent(String[] words, int k) {
Map<String,Integer> hashMap =new HashMap<>();
for(String s: words) {
if(hashMap.get(s) == null) {
hashMap.put(s,1);
}else {
hashMap.put(s,hashMap.get(s)+1);
}
}
//2、建立小根堆
PriorityQueue<Map.Entry<String,Integer>> minHeap = new PriorityQueue<>(
k, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
if(o1.getValue().compareTo(o2.getValue()) == 0) {
return o2.getKey().compareTo(o1.getKey());
}
return o1.getValue().compareTo(o2.getValue());
}
}
);
//3、遍歷hashMap 把里面 的數(shù)據 放到小根堆
for(Map.Entry<String,Integer> entry : hashMap.entrySet()) {
if(minHeap.size() < k) {
minHeap.offer(entry);
}else {
//小根堆放滿了K個,下一個entry和堆頂元素比較
Map.Entry<String,Integer> top = minHeap.peek();
//堆頂?shù)念l率小于當前entry的頻率 就出隊 然后入隊entry
if(top.getValue().compareTo(entry.getValue()) < 0) {
minHeap.poll();
minHeap.add(entry);
}else {
//頻率相同的情況
if(top.getValue().compareTo(entry.getValue()) == 0) {
if(top.getKey().compareTo(entry.getKey()) > 0) {
minHeap.poll();
minHeap.add(entry);
}
}
}
}
}
//4、 此時小根堆當中已經有了結果
//System.out.println(minHeap);
List<String> ret = new ArrayList<>();
for (int i = 0; i < k; i++) {
String key = minHeap.poll().getKey();
ret.add(key);
}
Collections.reverse(ret);
//System.out.println("ret: "+ret);
return ret;
}
}總結
到此這篇關于Java中Map和Set的常見用法舉例的文章就介紹到這了,更多相關Java Map和Set內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
在SpringBoot中實現(xiàn)一個訂單號生成系統(tǒng)的示例代碼
在Spring Boot中設計一個訂單號生成系統(tǒng),主要考慮到生成的訂單號需要滿足的幾個要求:唯一性、可擴展性、以及可能的業(yè)務相關性,本文給大家介紹了幾種常見的解決方案及相應的示例代碼,需要的朋友可以參考下2024-02-02
幾句話說清session,cookie和token的區(qū)別及說明
這篇文章主要介紹了幾句話說清session,cookie和token的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
SpringCloud-Alibaba-Sentinel服務降級,熱點限流,服務熔斷
這篇文章主要介紹了SpringCloud-Alibaba-Sentinel服務降級,熱點限流,服務熔斷,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12

