import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* 测试Map集合的常用方法
* add、remove、get、containKey、keySet、entrySet
*/
public class TestMap {
public static void main(String[] args) {
HashMap
// put的返回值是被替代的V,如果原来没有就是null
System.out.println(map.put("1001", "张三"));
System.out.println(map.put("1001", "张三丰"));
map.put("1002", "李四");
map.put("1003", "王五");
System.out.println(map);
// remove返回被删除的V,Key不存在则返回null
System.out.println(map.remove("1005"));
System.out.println(map.remove("1003"));
System.out.println(map);
// get返回key对应的value, 无则null
System.out.println(map.get("1002"));
System.out.println(map.get("1003"));
// containsKey 包含key返回true
System.out.println(map.containsKey("1002"));
System.out.println(map.containsKey("1003"));
System.out.println("遍历方式1:将所有key取出来存到set集合");
Set
for (String key : keys) {
System.out.println("K:"+key+" V:"+map.get(key));
}
System.out.println("遍历方式2:将所有Entry
for (Map.Entry
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
}
运行结果
Copy
null
张三
{1003=王五, 1002=李四, 1001=张三丰}
null
王五
{1002=李四, 1001=张三丰}
李四
null
true
false
遍历方式1:将所有key取出来存到set集合
K:1002 V:李四
K:1001 V:张三丰
遍历方式2:将所有Entry
1002=李四
1001=张三丰
HashMap(重点)#
基于哈希表的实现的Map接口。 此实现提供了所有可选的Map操作,并允许null值和null键。
底层原理#
底层是哈希表,查询速度超级快!
JDK8之前:数组+链表
JDK8之后:数组+链表 / 数组+红黑树
HashMap面试需要更深的理解,深入底层!
目前停留于了解原理,掌握运用。
测试代码#
存储自定义对象#
首先创建一个学生类
Copy
package map;
public class Student{
private int id;
private String name;
private int age;
public Student() {
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
Copy
package map;
import java.util.HashMap;
/**
* 若以自定义类作键存放,必须说明键重复的规则
* 实体类重写hashCode方法和equals方法
* 比如学生类中,同名不一定是同一个对象,同学号说明是同一个学生
*/
public class TestHashMap {
public static void main(String[] args) {
Student student1 = new Student(1001, "张三", 18);
Student student2 = new Student(1002, "李四", 17);
Student student3 = new Student(1001, "张三", 19);
Student student4 = new Student(1004, "李四", 20);
HashMap
map.put(student1, "第一名");
map.put(student2, "第二名");
map.put(student3, "第三名");
map.put(student4, "第四名");
System.out.println(map);
}
}
运行结果
Copy
{Student{id=1004, name='李四', age=20}=第四名, Student{id=1002, name='李四', age=17}=第二名, Student{id=1001, name='张三', age=18}=第一名, Student{id=1001, name='张三', age=19}=第三名}
此时观察student1 和 student3 的学号相同,对象重复,为什么还会添加到map中呢?
image-20200709162818912
有提示要重写Student类的hashCode方法和equals方法。
修改后的Student.java
Copy
package map;
import java.util.Objects;
public class Student{
private int id;
private String name;
private int age;
public Student() {
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
// ...get/set省略
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return id == student.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
再次运行测试类的代码得到的结果
Copy
{Student{id=1001, name='张三', age=18}=第三名, Student{id=1002, name='李四', age=17}=第二名, Student{id=1004, name='李四', age=20}=第四名}
分析:第一次添加1001学生时,value是第一名,再次遇到1001学生,覆盖了原来的value
深入理解HashMap#
更加详细的学习会写到另一篇
LinkedHashMap#
哈希表和链表实现的Map接口,具有可预测的迭代次序。有序集合!
Copy
public class LinkedHashMap
底层原理#
哈希表 -> 继承自HashMap
链表 -> 记录元素顺序
测试代码#
Copy
import java.util.*;
public class TestLinkedHashMap {
public static void main(String[] args) {
HashMap
map.put("1001", "张三");
map.put("1002", "李四");
map.put("1003", "王五");
map.put("1004", "赵六");
System.out.println("map的数据无序,取出的结果与存入顺序不一致:");
System.out.println(map);
System.out.println();
LinkedHashMap
link.put("1001", "张三");
link.put("1002", "李四");
link.put("1003", "王五");
link.put("1004", "赵六");
System.out.println("在map外层加一层链表记录元素存入的顺序:");
System.out.println(link);
}
}
运行结果
Copy
map的数据无序,取出的结果与存入顺序不一致:
{1004=赵六, 1003=王五, 1002=李四, 1001=张三}
在map外层加一层链表记录元素存入的顺序:
{1001=张三, 1002=李四, 1003=王五, 1004=赵六}
Hashtable#
Hashtable 和 Vector 一样都是上古级别的集合,在jdk1.2版本之后被更先进的HashMap及ArrayList集合取代。
值得注意的是,Hashtable的子类Properties目前依旧活跃着,它是唯一和IO流结合的集合。
Hashtable 与 HashMap 的区别#
底层都是哈希表,元老级更注重线程安全。
Hashtable
线程安全—>单线程集合—>速度慢
不能存储 null ,K,V都不可以
HashMap
线程不安全—>多线程集合—>速度快
可以存储 null
K 只能存储一个 null ,保证K的唯一性
V 可以存储任意个 null
测试代码#
Copy
import java.util.HashMap;
import java.util.Hashtable;
public class TestHashtable {
public static void main(String[] args) {
HashMap
map.put(null, null);
map.put("A", null);
map.put(null, "B");
System.out.println(map);
Hashtable
//table.put(null, null);//NullPointerException
//table.put("A", null);//NullPointerException
//table.put(null, "B");//NullPointerException
System.out.println(table);
}
}
运行结果
Copy
{null=B, A=null}
{}
HashMap中以null作为键时,再次添加null的键会覆盖之前的value值。
Hashtable只要涉及到null,就会报空指针异常。
集合练习#
判断字符串中某个字符出现的次数
Copy
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;
/**
* 判断字符串中某个字符出现的次数
*/
public class MapPractice {
public static void main(String[] args) {
Map
// Scanner sc = new Scanner(System.in);
// String str = sc.nextLine();
String str = "aaabbcdaabb";
char[] chars = str.toCharArray();
for (char ch : chars) {
if (map.get(ch) == null) {
map.put(ch, 1);
} else {
int count = map.get(ch);
map.put(ch, ++count);
}
}
// 使用linkedHashMap保证从前往后统计的顺序
System.out.println(map);
}
}
斗地主发牌
Copy
import java.util.*;
/**
* 扑克发牌并按大小排序
*/
public class PracticePoker {
public static void main(String[] args) {
// 存放索引的map,一个索引对应一张牌,按照一定规则定制
Map
// 存放牌色♠♥♦♣、数字的列表
List
List
Collections.addAll(colors, "♠", "♥", "♣", "♦");
Collections.addAll(numbers, "2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");
// 建立索引与牌的映射 0-"大王" 1-"小王" 2-"2♠" 3-"2♥" ...
int idx = 0;
map.put(idx++, "大王");
map.put(idx++, "小王");
for (String number : numbers) {
for (String color : colors) {
map.put(idx++, number + color);
//System.out.print(number + color + " ");
}
}
// 发牌发的是索引,便于比大小 , 因为set是无序的,不能洗牌
Set
ArrayList
// System.out.println("洗牌前" + idxList);
Collections.shuffle(idxList);
// System.out.println("洗牌后" + idxList);
// 玩家的牌用列表接收,便于排序
ArrayList
ArrayList
ArrayList
ArrayList
// 发牌
special.add(idxList.get(0));
special.add(idxList.get(1));
special.add(idxList.get(2));
for (int i = 3; i < idxList.size(); i++) {
if (i % 3 == 0) {
playA.add(idxList.get(i));
} else if (i % 3 == 1) {
playB.add(idxList.get(i));
} else if (i % 3 == 2) {
playC.add(idxList.get(i));
}
}
// 排序
Collections.sort(playA);
Collections.sort(playB);
Collections.sort(playC);
Collections.sort(special);
// 看牌
lookPoker("刘德华", playA, map);
lookPoker("周润发", playB, map);
lookPoker("周星驰", playC, map);
lookPoker("底牌", special, map);
}
public static void lookPoker(String name, List
System.out.print(name + "的牌为:");
for (Integer idx : idxList) {
System.out.print(poker.get(idx) + " ");
}
System.out.println();
}
}
亚马逊测评 www.yisuping.com