Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。
以前常见的搜索方式有:
上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中的查找比如:
可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了,本节介绍的Map和Set是一种适合动态查找的集合容器。
一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称之为Key-value的键值对,所以模型会有两种:
而 Map 中存储的就是 key-value 的键值对,Set 中只存储了 Key。
Map是STL [1] 的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。
Map是一个接口类,该类没有继承自Collection,该类中存储的是
Map.Entry
注意:Map.Entry
当我们通过get 获取的是Integer类型的值,让他赋值给整形会发生拆包,这里map中没有key,那么会返回null,此时拆包,就会发生空指针异常;
而使用 getOrDefault() , 如果map中没有key , 就会返回我们设置的默认值, 就不会进行拆包操作了 .
TreeMap继承关系图如下:
HashMap继承关系图如下:
Map当中没有实现Iterable接口, 所以遍历map的思路是将Map放到实现Iterable接口中的Set中即可;
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("三国演义", "罗贯中");
map.put("西游记", "吴承恩");
map.put("水浒传", "施耐庵");
map.put("红楼梦", "曹雪芹");
Set<String> set = map.keySet();
for (String s : set) {
System.out.println(s+" : "+map.get(s));
}
}
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("三国演义", "罗贯中");
map.put("西游记", "吴承恩");
map.put("水浒传", "施耐庵");
map.put("红楼梦", "曹雪芹");
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry.getKey()+" : "+entry.getValue());
}
}
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("三国演义", "罗贯中");
map.put("西游记", "吴承恩");
map.put("水浒传", "施耐庵");
map.put("红楼梦", "曹雪芹");
map.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String key, String value) {
System.out.println(key+" : "+value);
}
});
}
Set接口中大部分方法都是从Collection接口中扩展的,常用方法如下:
TreeSet继承关系图如下:
HashSet继承关系图如下:
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next()+" ");
}
}
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
for (Integer integer : set) {
System.out.print(integer + " ");
}
}
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
set.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.print(integer+" ");
}
});
}
class Solution {
public int singleNumber(int[] nums) {
HashSet<Integer> set = new HashSet<>();
for (int x : nums) {
if (set.contains(x)) {
set.remove(x);
} else {
set.add(x);
}
}
for (int i = 0; i < nums.length; i++) {
if (set.contains(nums[i])) {
return nums[i];
}
}
return -1;
}
}
使用Map,键值对为<已知链表结点地址, 复制链表结点地址>
第一次遍历已知链表时,创建复制链表对象的结点并将它们结点地址以<已知链表结点地址, 复制链表结点地址>键值对的形式存入Map中,然后第二次遍历已知链表,根据已知链表结点找到Map中对应复制链表的结点将复制链表结点链接起来
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
Node cur = head;
Map<Node, Node> map = new HashMap<>();
while (cur != null ) {
Node node = new Node(cur.val);
map.put(cur, node);
cur = cur.next;
}
cur = head;
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);
}
}
把宝石放入Set中,设置以恶搞计数器,然后遍历石头判断宝石是否包含石头的字符,含有计算器就++;
class Solution {
public int numJewelsInStones(String jewels, String stones) {
/*
Map map = new HashMap<>();
map.put(jewels, 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++;
}
}
}
*/
int count = 0;
HashSet<Character> set = new HashSet<>();
for (Character ch : jewels.toCharArray()) {
set.add(ch);
}
for (Character ch : stones.toCharArray()) {
if (set.contains(ch)) {
count++;
}
}
return count;
}
}
假设预期输出的字符串为strInput,实际输出的字符串为strOutput,我们可以使用Set来存储我们实际输出的字符串,然后在遍历预期输出的字符串,如果发现不含在strOutput里面的就输出。
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
public class Main {
public static void func(String strInput, String strOutput) {
Set<Character> set = new HashSet<>();
for (char ch : strOutput.toUpperCase().toCharArray()) {
set.add(ch);
}
Set<Character> badSet = new HashSet<>();
for (char ch : strInput.toUpperCase().toCharArray()) {
if(!badSet.contains(ch) && !set.contains(ch)) {
badSet.add(ch);
System.out.print(ch);
}
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String strInput = scanner.nextLine();
String strOutput = scanner.nextLine();
func(strInput, strOutput);
}
}
class Solution {
public static List<String> topKFrequent(String[] words, int k) {
HashMap<String,Integer> map = new HashMap<>();
//1、统计每个单词出现的次数 map
for (String s : words) {
if(map.get(s) == null) {
map.put(s,1);
}else {
int val = map.get(s);
map.put(s,val+1);
}
}
//2、建立一个大小为K的小根堆
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()-o2.getValue();
}
});
//3、遍历Map
for (Map.Entry<String,Integer> entry : map.entrySet()) {
if(minHeap.size() < k) {
minHeap.offer(entry);
}else {
//说明堆中 已经放满了K个元素,需要看堆顶元素的数据 和当前的数据的大小关系
Map.Entry<String,Integer> top = minHeap.peek();
//判断频率是否相同,如果相同,比较单词的大小,单词小 的入堆
if(top.getValue().compareTo(entry.getValue()) == 0) {
if(top.getKey().compareTo(entry.getKey()) > 0) {
minHeap.poll();
minHeap.offer(entry);
}
}else {
if(top.getValue().compareTo(entry.getValue()) < 0) {
minHeap.poll();
minHeap.offer(entry);
}
}
}
}
//System.out.println(minHeap);
List<String> ret = new ArrayList<>();
for (int i = 0;i < k;i++) {
Map.Entry<String,Integer> top = minHeap.poll();
ret.add(top.getKey());
}
Collections.reverse(ret);
return ret;
}
}