集合框架之map
一、map介绍
map集合中存放的都是一组组映射关系 key=value
一些常用方法简介:
1、增加
put(K key, V value)
putAll(Map<? extends K,? extends V> m)
2、删除
clear()
remove(Object key)
3、判断
containsKey(Object key)
containsValue(Object value)
isEmpty()
4、获取
get(Object key)
size()
values()
entrySet()
keySet()
其中关于Map的三个特别方法:
1、put(K key, V value) <K , V> k代表 key 键 V代表 value 值
①、put的新增用途
代码如下:
package com.songwanxi.Map;
import java.util.HashMap;
import java.util.Map;
/**
* 三个map集合中特别的方法:
* 1、put
* 2、entrySet
* 3、keySet
* @author 10570
*
*/
public class MapDemo {
public static void main(String[] args) {
//Map <K , V> k代表 key 键 V代表 value 值
Map map = new HashMap<>();
//1、put 新增用途
map.put("键", "值");
map.put("zs", 14);
map.put("ls", 24);
map.put("ww", 34);
map.put("zl", 22);
System.out.println(map);
}
}
运行结果:
② put 修改的用途(当容器中已经存在了的key,再次存放,那么会覆盖原有的key所对应的值value) 并且调用此方法,可以获取原来的key对应的值
代码如下:
package com.songwanxi.Map;
import java.util.HashMap;
import java.util.Map;
/**
* 三个map集合中特别的方法:
* 1、put
* 新增的用途
* 修改的用途(当容器中已经存在了的key,再次存放,那么会覆盖原有的key所对应的值value)
* 调用此方法,可以获取原来的key对应的值
* 2、entrySet
* 3、keySet
* @author 10570
*
*/
public class MapDemo {
public static void main(String[] args) {
//Map <K , V> k代表 key 键 V代表 value 值
Map map = new HashMap<>();
//1、put 新增用途
map.put("键", "值");
map.put("zs", 14);
map.put("ls", 24);
map.put("ww", 34);
map.put("zl", 22);
//2、修改的用途(当容器中已经存在了的key,再次存放,那么会覆盖原有的key所对应的值value) 调用此方法,可以获取原来的key对应的值
Object old = map.put("zs", 100);
System.out.println("修改前的value:"+old);
System.out.println(map);
}
}
运行结果如下:
2、entrySet 与 keySet
这两个方法是map集合中所特有的遍历方法;
了解:
HashMap是无序的,因为该集合的底层是Set集合做的;
关于HashMap与TreeMap
①HashMap 底层数据结构是 哈希表 允许使用null值和null键,该集合是不同步的
②TreeMap 底层数据结构是 二叉树 线程不同步,可以用于给Map集合中的键进行排序,(自然排序以及比较器排序)
2.1 entrySet:
代码:
package com.songwanxi.Map;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* 三个map集合中特别的方法:
* 1、put
* 新增的用途
* 修改的用途(当容器中已经存在了的key,再次存放,那么会覆盖原有的key所对应的值value)
* 调用此方法,可以获取原来的key对应的值
* 2、entrySet
* 3、keySet
* 这两个方法是map集合中所特有的遍历方法
*
* HashMap是无序的,集合的底层是set集合做的
*
* HashMap 数据结构 哈希表
* TreeMap 数据结构 二叉树
* 能够进行自然排序以及比较器排序
*
* map集合是不继承collection接口的
* 这也就意味着它不具备迭代器的方法
* @author 10570
*
*/
public class MapDemo {
public static void main(String[] args) {
//Map <K , V> k代表 key 键 V代表 value 值
Map map = new HashMap<>();
//1、put 新增用途
map.put("键", "值");
map.put("zs", 14);
map.put("ls", 24);
map.put("ww", 34);
map.put("zl", 22);
// map.put(null, null);
//2、修改的用途(当容器中已经存在了的key,再次存放,那么会覆盖原有的key所对应的值value)
// Object old = map.put("zs", 100);
//
// System.out.println("修改前的value:"+old);
// System.out.println(map);
//entrySet
System.out.println("entrySet方法测试:");
Set<Entry<Object, Object>> entrySet = map.entrySet();
for (Entry<Object, Object> entry : entrySet) {
System.out.println("Key:"+entry.getKey()+"---Value:"+entry.getValue());
}
}
}
结果:
2.2 keySet:
代码:
package com.songwanxi.Map;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* 三个map集合中特别的方法:
* 1、put
* 新增的用途
* 修改的用途(当容器中已经存在了的key,再次存放,那么会覆盖原有的key所对应的值value)
* 调用此方法,可以获取原来的key对应的值
* 2、entrySet
* 3、keySet
* 这两个方法是map集合中所特有的遍历方法
*
* HashMap是无序的,集合的底层是set集合做的
*
* HashMap 数据结构 哈希表
* TreeMap 数据结构 二叉树
* 能够进行自然排序以及比较器排序
*
* map集合是不继承collection接口的
* 这也就意味着它不具备迭代器的方法
* @author 10570
*
*/
public class MapDemo {
public static void main(String[] args) {
//Map <K , V> k代表 key 键 V代表 value 值
Map map = new HashMap<>();
//1、put 新增用途
map.put("键", "值");
map.put("zs", 14);
map.put("ls", 24);
map.put("ww", 34);
map.put("zl", 22);
// map.put(null, null);
//2、修改的用途(当容器中已经存在了的key,再次存放,那么会覆盖原有的key所对应的值value)
// Object old = map.put("zs", 100);
//
// System.out.println("修改前的value:"+old);
// System.out.println(map);
//entrySet
// System.out.println("entrySet方法测试:");
// Set<Entry<Object, Object>> entrySet = map.entrySet();
// for (Entry<Object, Object> entry : entrySet) {
// System.out.println("Key:"+entry.getKey()+"---Value:"+entry.getValue());
// }
//keySet
System.out.println("keySet方法测试:");
Set<Object> keySet = map.keySet();
for (Object key : keySet) {
System.out.println("key:"+key+"---value:"+map.get(key)/*通过key获取对应的value*/);
}
}
}
结果:
Map小结:
①Hashtable:底层是哈希表数据结构,不可以存入null键null值,该集合石线程同步的,jdk1.0,效率低
②HashMap:底层是哈希表数据结构,允许使用null值和null键,该集合是不同步的。将Hashtable替代;jdk1.2,效率高
③TreeMap:底层是二叉树数据结构,线程不同步,可以用于给Map集合中的键进行排序
注意:添加元素时,如果键已经在集合中存在,那么后添加的值会覆盖原来的值,并且put方法会将原有的值返回
二、Map应用
1、要求:
① 将学生作为键,地址作为值进行存储,名字年龄相同则被认定为一个人,最后输出
② 最后按年龄进行排序
③ 需求改变、按姓名进行排序
一点一点的来:
第一点:将学生作为键,地址作为值进行存储,名字年龄相同则被认定为一个人,最后输出
分析:
a、需要学生(封装学生类(属性:名字、年龄))
b、名字年龄相同则被认定为一个人(判断是否重复(重写hashcode、equals方法))
c、打印输出
代码:
package com.songwanxi.Map;
import java.util.HashMap;
import java.util.Set;
/**
* 要求:
*① 将学生作为键,地址作为值进行存储,名字年龄相同则被认定为一个人,最后输出
*分析:a、需要学生(封装学生类(属性:名字、年龄))
* b、名字年龄相同则被认定为一个人(判断是否重复(重写hashcode、equals方法))
* c、打印输出
*② 最后按年龄进行排序
*③ 需求改变、按姓名进行排序
* @author 10570
*
*/
public class Maptest1 {
public static void main(String[] args) {
HashMap map = new HashMap<>();
//TreeMap map = new TreeMap<>();
//这里key是学生类 value 是地址
map.put(new Stu("zs", 18), "上海");
map.put(new Stu("ls", 20), "北京");
map.put(new Stu("zs", 18), "长沙");
map.put(new Stu("zl", 30), "武汉");
map.put(new Stu("aw", 20), "广州");
Set key = map.keySet();
for (Object obj : key) {
System.out.println(obj);
}
}
}
class Stu {
private String name;
private int age;
public Stu(String name, int age) {
this.name = name;
this.age = age;
}
public Stu() {
super();
}
@Override
public String toString() {
return "Stu [name=" + name + ", age=" + age + "]";
}
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;
}
//重写hashcode
@Override
public int hashCode() {
// TODO 名字和年龄
return this.name.hashCode() + this.age;
}
//重写equals
@Override
public boolean equals(Object obj) {
// TODO 判断名字和年龄是否一致
Stu stu = (Stu) obj;
return this.name.equals(stu.getName()) && this.age == stu.getAge();
}
}
结果:
可以看到重复的 zl 18 只有一个
第二点:最后按年龄进行排序
分析:需要让学生类自身具备比较性,实现Comparable接口,完成compareTo方法
注:当年龄相等时(主要条件满足)就比较姓名(次要条件)
代码:
package com.songwanxi.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.TreeMap;
/**
* 要求:
*① 将学生作为键,地址作为值进行存储,名字年龄相同则被认定为一个人,最后输出
*分析:a、需要学生(封装学生类(属性:名字、年龄))
* b、名字年龄相同则被认定为一个人(判断是否重复(重写hashcode、equals方法))
* c、打印输出
*② 最后按年龄进行排序
* 分析:需要让学生类自身具备比较性,实现Comparable接口
*③ 需求改变、按姓名进行排序
* @author 10570
*
*/
public class Maptest1 {
public static void main(String[] args) {
//HashMap map = new HashMap<>();
TreeMap map = new TreeMap<>();
//这里key是学生类 value 是地址
map.put(new Stu("zs", 18), "上海");
map.put(new Stu("ls", 20), "北京");
map.put(new Stu("zs", 18), "长沙");
map.put(new Stu("zl", 30), "武汉");
map.put(new Stu("aw", 20), "广州");
Set key = map.keySet();
for (Object obj : key) {
System.out.println(obj);
}
}
}
class Stu implements Comparable<Stu>{
private String name;
private int age;
public Stu(String name, int age) {
this.name = name;
this.age = age;
}
public Stu() {
super();
}
@Override
public String toString() {
return "Stu [name=" + name + ", age=" + age + "]";
}
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;
}
//重写hashcode
@Override
public int hashCode() {
// TODO 名字和年龄
return this.name.hashCode() + this.age;
}
//重写equals
@Override
public boolean equals(Object obj) {
// TODO 判断名字和年龄是否一致
Stu stu = (Stu) obj;
return this.name.equals(stu.getName()) && this.age == stu.getAge();
}
//按年龄大小排序 当年龄相等(主要条件)时就按姓名字母排序(次要条件)
@Override
public int compareTo(Stu o) {
// TODO Auto-generated method stub
int num= this.age-o.age;
//System.out.println(num);
if(num == 0) {
return this.name.compareTo(o.name);
}
return this.age - o.age;
}
}
结果:
可以看到age从小到大排序,而当age相等时按name排序
第三点:需求改变、按姓名进行排序
分析:
改变的代码是不可取的,我们需要新增个比较器来完成需求
实现java.util.comparator
创建一个比较器
代码:
package com.songwanxi.Map;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Set;
import java.util.TreeMap;
/**
* 要求:
*① 将学生作为键,地址作为值进行存储,名字年龄相同则被认定为一个人,最后输出
*分析:a、需要学生(封装学生类(属性:名字、年龄))
* b、名字年龄相同则被认定为一个人(判断是否重复(重写hashcode、equals方法))
* c、打印输出
*② 最后按年龄进行排序
* 分析:需要让学生类自身具备比较性,实现Comparable接口
*③ 需求改变、按姓名进行排序
* 分析:
* 改变的代码是不可取的,我们需要新增个比较器来完成需求
* 实现java.util.comparator
* 创建一个比较器
* @author 10570
*
*/
public class Maptest1 {
public static void main(String[] args) {
//HashMap map = new HashMap<>();
//TreeMap map = new TreeMap<>();
TreeMap map = new TreeMap<>(new StuName());//使用比较器
//这里key是学生类 value 是地址
map.put(new Stu("zs", 18), "上海");
map.put(new Stu("ls", 20), "北京");
map.put(new Stu("zs", 18), "长沙");
map.put(new Stu("zl", 30), "武汉");
map.put(new Stu("aw", 20), "广州");
map.put(new Stu("zl", 45), "武汉");
Set key = map.keySet();
for (Object obj : key) {
System.out.println(obj);
}
}
}
/**
* 按姓名进行排序
* @author 10570
*
*/
class StuName implements Comparator<Stu>{
@Override
public int compare(Stu o1, Stu o2) {
int num = o1.getName().compareTo(o2.getName());
//System.out.println(num);
if(num == 0) {
return o1.getAge() - o2.getAge();
}
return num;
}
}
class Stu implements Comparable<Stu>{
private String name;
private int age;
public Stu(String name, int age) {
this.name = name;
this.age = age;
}
public Stu() {
super();
}
@Override
public String toString() {
return "Stu [name=" + name + ", age=" + age + "]";
}
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;
}
//重写hashcode
@Override
public int hashCode() {
// TODO 名字和年龄
return this.name.hashCode() + this.age;
}
//重写equals
@Override
public boolean equals(Object obj) {
// TODO 判断名字和年龄是否一致
Stu stu = (Stu) obj;
return this.name.equals(stu.getName()) && this.age == stu.getAge();
}
//按年龄大小排序 当年龄相等(主要条件)时就按姓名字母排序(次要条件)
@Override
public int compareTo(Stu o) {
// TODO Auto-generated method stub
int num= this.age-o.age;
//System.out.println(num);
if(num == 0) {
return this.name.compareTo(o.name);
}
return this.age - o.age;
}
}
结果:
可以看到主要按照name排序,当name相等时在按照age排序
注:.reversed()是比较器反转方法
2、统计字符串中字符出现次数,按次数排序
2.1首先了解一下String,StringBuffer,StringBuilder三者的区别?
String a = new String(“abc”);
这行代码在堆内存空间开辟的是两个空间:new String(“abc”); 和 “abc”
String b = ”abc”;
这行代码在堆内存空间开辟的是一个空间: “abc”
这是两者的不同
String a =”a”;
a+=”bcd”;
这行代码在内存里开辟两个空间:”a” 和 “abcd”
Int i=1;
i+=2;
这行代码在内存里开辟的就是一个空间:3
至于StringBuffer sb = new StringBuffer();
StringBuilder sb2= nwe StringBuilder();
是在内存中创建一个字符串的容器,而sb.append(“abc”);拼接是在这个字符串容器中拼接的,所以使用的都是一个空间,StringBuilder 同StringBuffer ,String拼接则是一直在创建新的空间,所以StringBuffer,StringBuilder比String更优越。
图解:
而对于StringBuffer与StringBuilder的区别?
StringBuffer 同步 安全
StringBuilder 异步 不安全
2.2 统计字符串中字符出现次数
给定一个字符串:asdfnjksadfnkaslkdlk
输出格式:a(4)b(9)…z(7)
分析:
①我们需要将字符a-z当作键key,将次数作为值value,也就是说我们需要创建一个map集合来做这些事情
②需要将待处理的字符串转成字符数组,以便于获取map集合的key
③如果该字符(a)第一出现的时候,就给a对应key映射值value赋值为1,如果说该字符再次出现,那么就是value+1;
④打印出a(4)b(9)…z(7)格式串,意味着需要对key进行排序
代码:
package com.songwanxi.Map;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
/**
* 统计字符串中字符出现次数
* asdfnjksadfnkaslkdlk
* a(4)b(9)...z(7)
* 1、分析结果得出结论:我们需要将字符a-z当作键key,将次数作为值value,
* 也就是说我们需要创建一个map集合来做这些事情
* 2、需要将待处理的字符串转成字符数组,以便于获取map集合的key
* 3、如果该字符(a)第一出现的时候,就给a对应key映射值value赋值为1
* 如果说该字符再次出现,那么就是value+1;
* 4、打印出a(4)b(9)...z(7)格式串
* 意味着需要对key进行排序
* @author 10570
*
*
*/
public class TreeMapDemo {
public static void main(String[] args) {
String str="asdfnjksadfnkaslkdlk";//给定一个字符串
//Map<Character, Integer> map = new HashMap<>();
TreeMap<Character, Integer> map = new TreeMap<>();//通过TreeMap进行排序
char [] charArray = str.toCharArray();//拆解字符串
for (char c : charArray) {
Integer obj = map.get(c);//每个字符作为key来查询对应的value(出现次数)
if(obj == null) {
//之前没有出现的,第一次出现 设置出现次数为1
map.put(c, 1);
}else {
//之前出现过的 出现次数加1
map.put(c, ++obj);
}
}
StringBuilder sb = new StringBuilder();
Set<Entry<Character, Integer>> entry = map.entrySet();
for (Entry<Character, Integer> en : entry) {
sb.append(en.getKey()+"("+en.getValue()+")");//拼接输出格式
}
System.out.println(sb);//输出
}
}
结果:
三、集合框架工具类(Collections、Arrays)
Arrays:
代码:
package com.songwanxi.Map;
import java.util.Arrays;
/**
* 集合框架工具类(Collections、Arrays)
* @author 10570
*
*/
public class UtilDemo {
public static void main(String[] args) {
int n [] = {1,2,3,4};
System.out.println(n);//无法直接打印内容 打印的是[I@7852e922
}
}
结果:
可以看到无法直接打印 数组内容 但是使用arrays提供的工具后:
代码:
package com.songwanxi.Map;
import java.util.Arrays;
/**
* 集合框架工具类(Collections、Arrays)
* @author 10570
*
*/
public class UtilDemo {
public static void main(String[] args) {
int n [] = {1,2,3,4};
System.out.println(n);//无法直接打印内容 打印的是[I@7852e922
//使用Arrays工具类
System.out.println(Arrays.toString(n));//可以直接打印内容 [1, 2, 3, 4]
//除了Arrays.toString(n)外 还有很多 二分搜索法/冒泡排序/选择排序/快速排序/希尔排序
}
}
结果:
能直接打印其数组内容了。
Collections:
代码:
package com.songwanxi.Map;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
/**
* 集合框架工具类(Collections、Arrays)
* @author 10570
*
*/
public class UtilDemo {
public static void main(String[] args) {
// int n [] = {1,2,3,4};
//System.out.println(n);//无法直接打印内容 打印的是[I@7852e922
//使用Arrays工具类
//System.out.println(Arrays.toString(n));//可以直接打印内容 [1, 2, 3, 4]
//除了Arrays.toString(n)外 还有很多 二分搜索法/冒泡排序/选择排序/快速排序/希尔排序
TreeMap<S, String> map = new TreeMap<>(new SAgeNameComp());
map.put(new S("zl", 15), "sh");
map.put(new S("zl", 14), "sh");
map.put(new S("ad", 20), "sh");
map.put(new S("dv", 35), "sh");
map.put(new S("hx", 12), "sh");
Set set = map.entrySet();
for (Object obj : set) {
System.out.println(obj);
}
}
}
/**
* 建一个比较器 主要条件是年龄从大到小 次要条件是姓名
* @author 10570
*
*/
class SAgeNameComp implements Comparator<S>{
@Override
public int compare(S o1, S o2) {
// TODO Auto-generated method stub
int num = o1.getAge() - o2.getAge();
if(num == 0) {
return o1.getName().compareTo(o2.getName());
}
return num;
}
}
class S {
private String name ;
private int age;
public S(String name, int age) {
super();
this.name = name;
this.age = age;
}
public S() {
super();
}
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 "S [name=" + name + ", age=" + age + "]";
}
}
结果:
可以看到确实是按照主要条件是年龄从大到小 次要条件是姓名的条件排序的
当我们的需求是上述条件的反面时怎么办?
再写一个比较器比较麻烦,可以直接采用collections里面的便捷方法reverseOrder() 代表反转
代码:
// TreeMap<S, String> map = new TreeMap<>(new SAgeNameComp());
TreeMap<S, String> map = new TreeMap<>(Collections.reverseOrder(new SAgeNameComp()));//使用collections里面的便捷方法
map.put(new S("zl", 15), "sh");
map.put(new S("zl", 14), "sh");
map.put(new S("ad", 20), "sh");
map.put(new S("dv", 35), "sh");
map.put(new S("hx", 12), "sh");
Set set = map.entrySet();
for (Object obj : set) {
System.out.println(obj);
}
}
}
结果:
我们可以很明显的看到两次的运行结果是相反的
Collections除了反转之外还有更多的方法,可以自行探索。
四、集合框架拓展(增强for循环、可变参数、静态导入)
1、增强for循环 foreach
增强型for循环的使用相较来说比较便捷,如果只是操作集合中元素的而不使用索引的话,建议用此方法。
对于普通for循环,如果需要使用索引进行其它操作的话,建议用这个。
两者的区别:
①增强for循环必须有被遍历的目标(如集合或数组)。
②普通for循环遍历数组的时候需要索引。
③增强for循环不能获取下标,所以遍历数组时最好使用普通for循环。
增强型for循环的特点:
代码少、简洁、对集合进行遍历,只能获取集合元素,不能对集合进行操作,类似迭代器的简写形式,但是相较而言迭代器还可以对元素进行remove操作(甚至ListIterator可以进行增删改查的操作)。
写法:
for(数据类型变量名 :被遍历的集合(collection)或者数组) {
执行语句
}
代码:
package com.songwanxi.Map;
/**
* 四、集合框架拓展(增强for循环、可变参数、静态导入)
* @author 10570
*
*/
public class TzDemo {
public static void main(String[] args) {
//增强for循环
//列如一个int数组
int n [] = {1,2,3,4,5};
for (int i : n) {
System.out.println(i);
}
}
}
结果:
2、可变参数
可变参数:可变参数的本质是动态的、可变的
写法:
修饰符 返回值类型 方法名(数据类型…变量名){ // 可变参数,形参列表可以接受0个值 ,也可以接//受n个值
方法体
}
例如:
package com.songwanxi.Map;
/**
* 四、集合框架拓展(增强for循环、可变参数、静态导入)
* @author 10570
*
*/
public class TzDemo {
public static void main(String[] args) {
//增强for循环
//列如一个int数组
// int n [] = {1,2,3,4,5};
// for (int i : n) {
// System.out.println(i);
// }
//调用可变参数写的方法
System.out.println(getcb(6));
}
//可变参数
public static int getcb(int x){
int n = 1;
for (int i = 0; i < x; i++) {
n+=i;
}
return n;
}
}
结果:
注意:
① 可变参数本质就是一个数组,arr就是一个数组的引用地址(反编译工具查看源代码)
②一个方法 可以有可变参数和普通参数,但是可变参数必须放到参数列表末尾;
③一个方法 有且只能有一个可变参数;
3、静态导入 importStatic
可以直接导入到方法
写法:import static 包名….类名.方法名;
静态导入的注意事项:
A:方法必须是静态的
B:如果有多个同名的静态方法,容易不知道使用谁?这个时候要使用,必须加前缀。由此可见,意义不大,所以一般不用,但是要能看懂。
C:当类重名时,需要指定具体的包名。当方法重名时,需要指定具体的类或对象名。
当我导入import static java.util.Arrays.*;
//导入Arrays这个类中的所有静态成员,调用该类静态方法时,直接调用