文章目录
Java集合—双列集合系列
特点
- 双列集合一次需要存一对数据,分别为键和值
- 键不能重复,值可以重复
- 键和值一一对应,每个键只能找自己的对应的值
- 键+值这个整体我们成为“键值对”或者“键值对对象”,在Java中叫“Entry对象”
18.12 Map系列集合
Map是双列集合的顶层接口,它的功能是全部双列集合都可以继承使用的。
方法名称 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
//1.创建Map集合的对象
Map<String,String> m = new HashMap<>();
//2.添加元素
m.put("兔子","胡萝卜");
m.put("狗","骨头");
m.put("羊","草");
//put方法的细节:
//添加/覆盖
//在添加数据的时候,如果键不存在,那么直接把键值对对象添加到map集合当中,方法返回null
//在添加数据的时候,如果键是存在的,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回。
String value1 = m.put("青蛙", "虫子");//null
System.out.println(value1);
String value2 = m.put("狗", "肉");
System.out.println(value2);//骨头
System.out.println(m);
//{青蛙=虫子, 兔子=胡萝卜, 狗=肉, 羊=草}
//3.删除,根据键删除键值对元素
String value3 = m.remove("狗");
System.out.println(value3);//肉
System.out.println(m);//{青蛙=虫子, 兔子=胡萝卜, 羊=草}
// 4.移除所有的键值对元素
//m.clear();
//System.out.println(m);//{}
// 5.判断集合是否包含指定的键
boolean key1 = m.containsKey("青蛙");
System.out.println(key1);//true
// 6.判断集合是否包含指定的值
boolean value4 = m.containsValue("虫子");
System.out.println(value4);//true
// 7.判断集合是否为空
boolean isempty = m.isEmpty();
System.out.println(isempty);//false
// 8.集合的长度,也就是集合中键值对的个数
int size = m.size();
System.out.println(size);//3
}
}
18.13 Map的三种遍历方式
18.13.1键找值
思路:
- 首先,需要把键全部放在一个单例集合,用keySet()方法
- 然后,遍历单列集合得到每一个键,再通过get()方法找到每个键对应的值
- 输出
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
public class Test {
public static void main(String[] args) {
//1.创建Map集合的对象
Map<String,String> m = new HashMap<>();
//2.添加元素
m.put("兔子","胡萝卜");
m.put("狗","骨头");
m.put("羊","草");
//3.通过键找值
//3.1获取所有的键,把这些键放到一个单列集合当中
Set<String> keys = m.keySet();
//3.2增强for遍历装着键的单列集合,得到每一个键
for (String key : keys) {
//3.3 利用map集合中的键获取对应的值 get
String s = m.get(key);
System.out.println(key+"="+s);
}
// 迭代器遍历装着键的单列集合
Iterator<String> it = keys.iterator();
while (it.hasNext()){
String key1 = it.next();
// System.out.println(s);//获取键
//键通过get 方法获取对应的值
String value1 = m.get(key1);
System.out.println(key1+"="+value1);
//兔子=胡萝卜 狗=骨头 羊=草
}
//Lambda表达式遍历装着键的单列集合
keys.forEach(new Consumer<String>() {
@Override
public void accept(String key2) {
// System.out.println(key2);
String value2 = m.get(key2);
System.out.println(key2+"="+value2);
//兔子=胡萝卜 狗=骨头 羊=草
}
});
}
}
18.13.2 键值对
思路:
- 通过entrySet()方法获取所有的键值对对象,放在Set集合中,集合名字为entries
- 遍历entries这个集合,去得到里面的每一个键值对对象entry
- 利用entry调用getKey()和getValue()方法获取键和值(或者直接输出entry)
- 输出
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
public class Test02 {
public static void main(String[] args) {
// Map的第2种遍历方式:键值对,
//1.创建Map集合的对象
Map<String,String> map =new HashMap<>();
//2.添加元素
map.put("兔子","胡萝卜");
map.put("狗","骨头");
map.put("羊","草");
// 3.遍历,通过键值对对象进行遍历
//通过一个方法获取所有的键值对对象,返回一个Set集合
Set<Map.Entry<String, String>> entries = map.entrySet();
//3.1 增强for遍历装着键值对的单列集合entries,去得到里面的每一个键值对对象
for (Map.Entry<String, String> entry : entries) {
// System.out.println(entry);
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"="+value);
}
// 3.2迭代器遍历装着键值对的单列集合
Iterator<Map.Entry<String, String>> it = entries.iterator();
while (it.hasNext()){
Map.Entry<String, String> next = it.next();
// System.out.println(next);
String key = next.getKey();
String value = next.getValue();
System.out.println(key+"="+value);
}
//3.3Lambda表达式遍历装着键值对的单列集合
entries.forEach(new Consumer<Map.Entry<String, String>>() {
@Override
public void accept(Map.Entry<String, String> s) {
// System.out.println(s);
String key = s.getKey();
String value = s.getValue();
System.out.println(key+"="+value);
}
});
}
}
18.13.3 Lambda表达式
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
public class Test03 {
public static void main(String[] args) {
// Map的第三种遍历方式
// 创建集合的对象
Map<String,String> map = new HashMap<>();
// 添加元素
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);
}
});
// Lambda表达式遍历
//底层:
//forEach其实就是利用第二种方式进行遍历,依次得到每一个键和值
//再调用accept方法
map.forEach((String key, String value)-> {
System.out.println(key+"=>"+value);
}
);
}
}
18.14 HashMap
18.14.1 介绍
-
特点都是由键决定的:无序、不重复、无索引
-
HashMap是Map里面的一个实现类
-
没有额外的特有方法,直接使用Map里的方法就可以
-
HashMap跟HashSet底层原理一样,都是哈希表
-
依赖hashCode和equals方法保证键的唯一
-
如果键存储的是自定义对象,需要重写hashCode方法和equals方法
-
如果键存储自定义对象,不需要重写hashCode和equals方法
在存数据时,是利用键计算哈希值,跟值无关,如果要插入的位置有元素,put()方法则会覆盖原有的元素。
18.14.2 Test
test01 存储自定义对象
需求:创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)。
存储三个键值对元素,并遍历
要求:同姓名,同年龄认为是同一个学生
public class Student {
private String name;
private int age;
public Student() {}
public Student(String name, int age) {
this.name = name;
this.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;}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
public class StudentTest {
public static void main(String[] args) {
/*
需求:创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)。
存储三个键值对元素,并遍历
要求:同姓名,同年龄认为是同一个学生
*/
// 创建学生对象
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("lisi",24);
Student s3 = new Student("wangwu",25);
Student s4 = new Student("wangwu",25);
//1.创建HashMap的对象
HashMap<Student,String> hm = new HashMap<>();
hm.put(s1,"A省");
hm.put(s2,"B省");
hm.put(s3,"c省");
hm.put(s4,"D省");//会覆盖s1
// 1.键找值
// 把所有的键放在一个单列集合
Set<Student> keys = hm.keySet();
for (Student stuKey : keys) {
String stuValue = hm.get(stuKey);
System.out.println(stuKey+"=>"+stuValue);
}
//2. 键值对
Set<Map.Entry<Student, String>> entries = hm.entrySet();
for (Map.Entry<Student, String> entry : entries) {
Student key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"=>"+value);
}
//3. Lambda表达式
hm.forEach(new BiConsumer<Student, String>() {
@Override
public void accept(Student student, String s) {
System.out.println(student+"=>"+s);
}
});
}
}
test02 利用HashMap统计
18.15 LinkedHashMap
(可直接使用Map里面的方法)
- 由键决定:有序、不重复、无索引
- 有序指:存储和取出元素的顺序是一致的。
原理:
底层数据结构依然是哈希表,只是每个键值对元素又额外多了一个双链表的机制记录存储顺序,保证存和取的顺序一样。
import java.util.LinkedHashMap;
public class Test {
public static void main(String[] args) {
// 创建集合
LinkedHashMap<String,Integer> lhp = new LinkedHashMap<>();
// 添加元素
lhp.put("a",123);
lhp.put("a",666);
lhp.put("b",456);
lhp.put("c",789);
// 打印元素
System.out.println(lhp);//{a=666, b=456, c=789}
}
}
18.16 TreeMap
18.16.0概述
- TreeMao跟TreeSet底层原理一样,都是红黑树结构,增删改查性能较好。
- 由键决定特定:不重复、无索引、可排序
- 可排序:对键进行排序
- 注意:默认按照键的从小到大进行排序的,也可以自己规定键的排序规则。
18.16.1排序规则
-
实现
Comparable
接口,指定比较规则 -
创建集合时传递
Comparator
比较器对象,指定比较规则新的统计思想:利用map集合进行统计 如果题目中没有要求对结果进行排序,默认使用HashMap 如果题目中要求对结果进行排序,请使用TreeMap 键:表示要统计的内容 值:表示次数
18.16.2案例
test01 排序
TreeMap集合:基本应用
需求1:
键:整数表示id
值:字符串表示商品名称
要求1:按照id的升序排列
要求2:按照id的降序排列
import java.util.TreeMap;
public class Test {
public static void main(String[] args) {
// 创建集合对象
//Integer Double 默认情况下都是按照升序排列的
//String 按照字母再ASCII码表中对应的数字升序进行排列
TreeMap<Integer,String> hm =new TreeMap<>();
// 添加元素
hm.put(5,"可乐");
hm.put(2,"雪碧");
hm.put(1,"酸梅汤");
hm.put(6,"哇哈哈");
//默认按照键的从小到大进行排序的
System.out.println(hm);
//{1=酸梅汤, 2=雪碧, 5=可乐, 6=哇哈哈}
}
}
自己指定规则排序
import java.util.Comparator;
import java.util.TreeMap;
public class Test02 {
public static void main(String[] args) {
// 创建集合对象
TreeMap<Integer,String> hm =new TreeMap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//o1:当前要添加的元素
//o2:表示已经在红黑树中存在的元素
return o2-o1;
}
});
// 添加元素
hm.put(5,"可乐");
hm.put(2,"雪碧");
hm.put(1,"酸梅汤");
hm.put(6,"哇哈哈");
System.out.println(hm);
//{6=哇哈哈, 5=可乐, 2=雪碧, 1=酸梅汤}
}
}
test02 键的位置是自定义对象
TreeMap集合:基本应用
需求2:
键:学生对象 值:籍贯
要求:按照学生年龄的升序排列,年龄一样按照姓名的字母排列,同姓名 年龄视为同一个人。
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
//this:表示当前要添加的元素
//o:表示已经在红黑树中存在的元素
//返回值:
//负数:表示当前要添加的元素是小的,存左边
//正数:表示当前要添加的元素是大的,存右边
//0:表示当前要添加的元素已经存在,舍弃
int i = this.getAge() - o.age;
//compareTo()方法,默认按照字符在ASCll码表中的数字升序进行排序
int i1 = i == 0 ? this.getName().compareTo(o.name) : i;
return i1;
}
}
import java.util.TreeMap;
public class StudentTest {
public static void main(String[] args) {
// 创建集合对象
TreeMap<Student ,String> hm = new TreeMap<>();
// 创建学生对象
Student s1 = new Student("zhangsan",23);
Student s2 = new Student("lisi",24);
Student s3 = new Student("wangwu",25);
Student s4 = new Student("ahaoliu",23);
// 添加元素给集合
hm.put(s1,"河南");
hm.put(s2,"山东");
hm.put(s3,"南京");
hm.put(s4,"上海");
//System.out.println(hm);
Set<Student> keys = hm.keySet();
for (Student key : keys) {
String value = hm.get(key);
System.out.println(key+"=>"+value);
//Student{name='ahaoliu', age=23}=>上海
//Student{name='zhangsan', age=23}=>河南
//Student{name='lisi', age=24}=>山东
//Student{name='wangwu', age=25}=>南京
}
}
}
test03 TreeMap统计
需求:
字符串“aababcabcdabcde”
请统计字符串中每一个字符出现的次数,并按照以下格式输出
输出结果:
a(5)b(4)c(3)d(2)e(1)
import java.util.StringJoiner;
import java.util.TreeMap;
import java.util.function.BiConsumer;
public class Test {
public static void main(String[] args) {
String s = "aababcabcdabcde";
// 创建集合对象
TreeMap<Character,Integer> tm = new TreeMap<>();
for (int i = 0; i < s.length(); i++) {
// 获取字符串中每一个字母
char c = s.charAt(i);
// 拿着c代表的字符去集合中判断是否存在
if(tm.containsKey(c)){
// 字符存在,表示又出现了一次;
// 先把存在的次数拿出来
// 获取字母在集合中出现的次数
int count = tm.get(c);
// 存在表示字符又出现了一次
count++;
//再次的出现后,添加到集合中
tm.put(c,count);
}else {
// 字符在集合中不存在,表示该字符时第一次出现
// 则添加该字符
tm.put(c,1);
}
}
// 打印
System.out.println(tm);//{a=5, b=4, c=3, d=2, e=1}
// StringBuilder拼接
StringBuilder sb = new StringBuilder();
tm.forEach(new BiConsumer<Character, Integer>() {
@Override
public void accept(Character key, Integer value) {
sb.append(key).append("(").append(value).append(")");
}
});
System.out.println(sb);//a(5)b(4)c(3)d(2)e(1)
System.out.println();
// StringJoiner拼接
StringJoiner sj = new StringJoiner("","","");
tm.forEach(new BiConsumer<Character, Integer>() {
@Override
public void accept(Character key, Integer value) {
sj.add(key+"").add("(").add(value+"").add(")");
}
});
System.out.println(sj);//a(5)b(4)c(3)d(2)e(1)
}
}