Java基础——集合体系Map详解
上文中我们了解了集合体系中的单列集合:Java基础——集合以及Java集合——Set详解,接下来呢就让阿Q带大家一起学习一下双列集合map的使用吧。
集合体系
单列集合顶层接口 遍历方式:增强for,Iterator,集合转数组
Collection
|-List 存取有序,元素可以重复,有序就有索引,有索引就可以通过索引操作元素。遍历方式:普通for,增强for,Iterator,ListIterator,集合转数组
|-ArrayList 不安全,效率高,数组结构:增删慢;查询快
|-LinkedList不安全,效率高,链表结构:增删快;查询慢
|-Vector 数组结构 安全 效率低
|-Set 存取无序,元素唯一。遍历方式:增强for,Iterator,集合转数组
|-HashSet 哈希算法 哈希结构 存取无序 元素唯一
|-LinkedHashSet
|-TreeSet 二叉树算法可以排序
双列集合
Map双列集合,键唯一,值可以重复,遍历方式:根据键找值,根据键值对找键和值
|-HashMap底层的哈希结构 保证键的唯一
|-LinkedHashMap:存入和取出的顺序相同,同时键也是通过哈希算法保证元素唯一性的
|-TreeMap底层的二叉树结构 保证键的排序和唯一
Map:双列集合
特点:Map双列集合,Collection是单列集合;键不可以重复,值可以重复;数据结构针对键有效。
方法:
- 添加功能:V put(K key,V value):添加元素,如果键是第一次存储,就直接存储元素,返回null;如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值。
- 删除功能:void clear():移除所有的键值对元素;V remove(Object key):根据键删除键值对元素,并把值返回。
- 判断功能:boolean containsKey(Object key):判断集合是否包含指定的键;boolean containsValue(Object value):判断集合是否包含指定的值;boolean isEmpty():判断集合是否为空
- 获取功能:Set<Map.Entry<K,V>> entrySet():获取所有的键值对的集合;V get(Object key):根据键获取值;Set<K> keySet():获取集合中所有键的集合;Collection<V> values():获取集合中所有值的集合
- 长度功能:int size():返回集合中的键值对的个数
代码演示:
public static void demo1() {
Map<String, Integer> map = new HashMap<>();
Integer i1 = map.put("张三", 23); //如果键是第一次存储,就直接存储元素,返回null
Integer i2= map.put("李四", 24);
Integer i3 = map.put("王五", 25);
Integer i4 = map.put("赵六", 26);
Integer i5 = map.put("张三", 26); //相同的键不存储,值覆盖,把被覆盖的值返回
System.out.println(map); //{赵六=26, 张三=26, 李四=24, 王五=25}
System.out.println(i1); //null
System.out.println(i2); //null
System.out.println(i3); //null
System.out.println(i4); //null
System.out.println(i5); //23如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
}
public static void demo2() {
Map<String, Integer> map = new HashMap<>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25);
map.put("赵六", 26);
Integer value = map.remove("张三"); //根据键删除元素,返回键对应的值
System.out.println(value); //23
System.out.println(map.containsKey("张三")); //false 判断是否包含传入的键
System.out.println(map.containsValue(100)); //false 判断是否包含传入的值
System.out.println(map); //{赵六=26, 李四=24, 王五=25}
}
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25);
map.put("赵六", 26);
Collection<Integer> c = map.values(); //获取集合中所有值的集合
System.out.println(c); //[26, 23, 24, 25]
System.out.println(map.size()); //4
}
遍历
方式一:通过keySet()获取所有键的集合,然后遍历键的集合,获取到每一个键,然后通过getKey()方法获取每一个值,这样把键和值一一对应的找出来了,就遍历了集合。需要用到的方法:V get(Object key):根据键获取值,
Set<K> keySet():获取集合中所有键的集合。
代码演示:
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25);
map.put("赵六", 26);
//获取所有的键
/*
Set<String> keySet = map.keySet(); //获取所有键的集合
Iterator<String> it = keySet.iterator(); //获取迭代器
while(it.hasNext()) { //判断集合中是否有元素
String key = it.next(); //获取每一个键
Integer value = map.get(key); //根据键获取值
System.out.println(key + "=" + value);
}
*/
//使用增强for循环遍历
for(String key : map.keySet()) { //map.keySet()是所有键的集合
System.out.println(key + "=" + map.get(key));
}
}
方式二:通过entrySet()获取所有的键值对的集合,然后遍历键值对的集合,获取每一个键值对,然后根据键值对来获取每一个键和值,这样把键和值一一对应的找出来了,就遍历了集合。需要用到的方法:Set<Map.Entry<K,V>> entrySet():获取所有的键值对的集合,Map.Entry接口里面的K getKey():获取键值对中的键,V getValue():获取键值对中的值。
代码演示:
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25);
map.put("赵六", 26);
//Map.Entry说明Entry是Map的内部接口,将键和值的键值对封装成了Entry对象,并存储在Set集合中
/*
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
//获取每一个 键值对 对象
Iterator<Map.Entry<String, Integer>> it = entrySet.iterator();
while(it.hasNext()) {
//获取每一个键值对对象
Map.Entry<String, Integer> en = it.next(); //父类引用指向子类对象
//Entry<String, Integer> en = it.next(); //直接获取的是子类对象
String key = en.getKey(); //根据键值对对象获取键
Integer value = en.getValue(); //根据键值对对象获取值
System.out.println(key + "=" + value);
}
*/
for(Entry<String, Integer> en : map.entrySet()) {
System.out.println(en.getKey() + "=" + en.getValue());
}
}
Map.Entry原理:Entry是Map接口里面的一个内部接口, 他的实现类是Map子类里面的一个静态的内部类
HashMap
键是如何保证唯一性的呢? 通过哈希算法。 HashSet底层就是通过HashMap的键来完成的,所以说HashMap保证键的唯一性必须要让键的元素所在的类去重写 hashCode()和 equals()方法。
代码演示:
public static void main(String[] args) {
HashMap<Student, String> hm = new HashMap<>();
//Student作为键,必须重写equals和hashCode() 才能让HashMap保证键的唯一
hm.put(new Student("张三", 23), "北京");
hm.put(new Student("张三", 23), "上海");
hm.put(new Student("李四", 24), "广州");
hm.put(new Student("王五", 25), "深圳");
System.out.println(hm); //{Student [name=张三, age=23]=上海, Student [name=李四, age=24]=广州, Student [name=王五, age=25]=深圳} 去掉了重复的 "张三", 23
}
public class Student{ //重写了equals和HashCode的 Student类
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() { //重写hashCode方法
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) { //重写equals方法
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
LinkedHashMap
存入和取出的顺序相同,同时键也是通过哈希算法保证元素唯一性的。
public static void main(String[] args) {
LinkedHashMap<String, Integer> lhm = new LinkedHashMap<>();
lhm.put("张三", 23);
lhm.put("李四", 24);
lhm.put("赵六", 26);
lhm.put("王五", 25);
System.out.println(lhm);//{张三=23, 李四=24, 赵六=26, 王五=25} 存取有序
}
TreeMap
键是如何保证排序和唯一的呢?通过二叉树算法,TreeSet的底层就是通过TreeMap的键来完成的。
方式一:所以TreeMap保证键的唯一和排序需要让 TreeMap的键的元素所在的类去实现自然排序Comparable接口;
方式二:TreeMap保证键的唯一和排序 需要调用TreeMap的有参构造,传入一个比较器 Comparator
代码演示:
public static void main(String[] args) {
TreeMap<Student, String> tm = new TreeMap<>(new Comparator<Student>() {
//用TreeMap的有参构造方法 传入比较器
@Override
public int compare(Student s1, Student s2) {
int num = s1.getName().compareTo(s2.getName()); //按照姓名比较
return num == 0 ? s1.getAge() - s2.getAge() : num;
}
});
tm.put(new Student("张三", 23), "北京");
tm.put(new Student("李四", 13), "上海");
tm.put(new Student("赵六", 43), "深圳");
tm.put(new Student("王五", 33), "广州");
System.out.println(tm);
}
public static void demo1() {
TreeMap<Student, String> tm = new TreeMap<>(); //用TreeMap的无参构造方法创建的对象
//Student作为键,必须让Student实现Comparable接口 并且重写CompareTo方法
tm.put(new Student("张三", 23), "北京");
tm.put(new Student("李四", 13), "上海");
tm.put(new Student("王五", 33), "广州");
tm.put(new Student("赵六", 43), "深圳");
System.out.println(tm);
}
//Student实现了Comparable接口 重写了CompareTo方法
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) { // 重写了CompareTo方法
int num = this.age - o.age; //以年龄为主要条件
return num == 0 ? this.name.compareTo(o.name) : num;
}
}
HashMap的嵌套
学校一年级学生HashMap
键:一年级对象HashMap 值:班号1
键:学生对象 值:归属地
键:学生对象 值:归属地
键:一年级对象HashMap 值:班号2
键:学生对象 值:归属地
键:学生对象 值:归属地
代码演示:
public static void main(String[] args) {
//定义一年级1班
HashMap<Student, String> hm1 = new HashMap<>();
hm1.put(new Student("张三", 23), "北京");
hm1.put(new Student("李四", 24), "北京");
hm1.put(new Student("王五", 25), "上海");
hm1.put(new Student("赵六", 26), "广州");
//定义一年级2班
HashMap<Student, String> hm2 = new HashMap<>();
hm2.put(new Student("唐僧", 1023), "北京");
hm2.put(new Student("孙悟空",1024), "北京");
hm2.put(new Student("猪八戒",1025), "上海");
hm2.put(new Student("沙和尚",1026), "广州");
//定义双元课堂
HashMap<HashMap<Student, String>, String> hm = new HashMap<>();
hm.put(hm1, "一年级1班");
hm.put(hm2, "一年级2班");
//遍历双列集合
for(HashMap<Student, String> h : hm.keySet()) {
//hm.keySet()代表的是一年级所有的班级集合 h是每个班级
String value = hm.get(h); //get(h)根据班级对象获取班级代号value
//遍历键的双列集合对象
for(Student key : h.keySet()) { //h.keySet()代表班级中所有的学生对象集合,key是每个学生
String value2 = h.get(key); // h.get(key) 根据学生对象找到学生的所在地value2
System.out.println(key + "=" + value2 + "=" + value);
}
}
}
HashMap和Hashtable
1:HashMap线程不安全,效率高,1.2出现的;Hashtable线程安全的,效率低,1.0出现的。
2:HashMap可以存null键或null值,Hashtable不可以存储null键或null值。
代码演示:
public static void main(String[] args) {
HashMap<String, Integer> hm = new HashMap<>();
hm.put(null, 23); //不报错
hm.put("李四", null); //不报错
System.out.println(hm); //{null=23, 李四=null}
/*
Hashtable<String, Integer> ht = new Hashtable<>();
ht.put(null, 23); //编译不报错,运行报错java.lang.NullPointerException
ht.put("张三", null); //编译不报错,运行报错java.lang.NullPointerException
System.out.println(ht);
*/
}
Collections
public static <T extends Comparable<? super T>> void sort(List<T> list);排序:只能给List排序,而且要求被排序的List集合里面存储的元素必须实现Comparable接口(对象只有实现了Comparable接口,才能说明对象具备比较性)
public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key);在已经排好序的list集合中查找key的索引,List集合中的元素必须实现Comparable接口
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll);给求Collection集合中最大的元素,Collection集合的元素必须实现了Comparable接口,因为如果元素不具备比较性 我如何知道谁最大呢?
public static void reverse(List<?> list);把list集合反转,list集合里面的元素没有任何要求
public static void shuffle(List<?> list);把list集合里面的元素打乱顺序,list集合里面的元素没有任何要求
代码演示:
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("c");
list.add("d");
list.add("g");
list.add("f"); //String类实现了Comparable接口重写了CompareTo方法,所以可以求最大值
//根据list集合存的元素里面的CompareTo方法的比较规则 获取集合中的最大值
System.out.println(Collections.max(list));//g
Collections.reverse(list); //反转集合
Collections.shuffle(list); //随机置换,可以用来洗牌
System.out.println(list); //[g, f, d, a, c]
}
public static void demo() {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("c");
list.add("d");
list.add("f");
list.add("g");
Collections.sort(list); //进行二分法查找之前,必须先把list集合排序
System.out.println(Collections.binarySearch(list, "c")); //1
System.out.println(Collections.binarySearch(list, "b")); //-2
}
好了集合的全部内容就先总结到这了,以后如果遇到什么问题再继续更新。想了解更多学习知识,请关注微信公众号“阿Q说”,获取更多学习资料吧!你也可以后台留言说出你的疑惑,阿Q将会在后期的文章中为你解答。每天学习一点点,每天进步一点点。