目录
一、 双列集合
二、 Map
1. Map的常见API
2. Map的遍历方式
2.1 第一种遍历方式:键找值
2.2 第二种遍历方式:键值对
2.3 第三种遍历方式:Lambda表达式
三、 Map的三种实现类
1. HashMap
2. LinkedHashMap
3. TreeMap
3.1 练习: TreeMap基本应用
3.2 练习:统计个数
四、 可变参数
五、Collections
我们通过单列集合和双列集合的对比进行学习:
双列集合特点:
- 双列集合一次需要存入一对数据,分别为键和值。
- 键不能重复,值可以重复。
- 键和值是一 一对应的,每一个键只能找到自己对应的值。
- 键 + 值这个整体 我们称之为“键值对” 或者 “键值对对象”,在Java中叫做Entry对象“”。
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() | 集合的长度,也就是集合中键值对的个数 |
public class MapTest {public static void main(String[] args) {// 1.创建map集合对象Map m = new HashMap<>();// 2.添加元素// 在添加数据时,如果键不存在,则直接将键值对对象添加到map集合中// 如果键存在,则会将原有键值对对象覆盖,并且被覆盖的值会进行返回String value1 = m.put("郭靖", "黄蓉"); // nullSystem.out.println(value1);m.put("韦小宝", "沐剑屏");m.put("尹志平", "小龙女");String value2 = m.put("韦小宝", "双儿");System.out.println(value2); // 沐剑屏// 打印集合System.out.println(m); // {韦小宝=双儿, 尹志平=小龙女, 郭靖=黄蓉}// 3.删除元素String result = m.remove("郭靖");System.out.println(result); // 黄蓉// 4.判断是否包含boolean keyResult = m.containsKey("郭靖");System.out.println(keyResult); // falseboolean valueResult = m.containsValue("小龙女");System.out.println(valueResult); // true// 5.判断集合是否为空boolean result1 = m.isEmpty();System.out.println(result1); // false// 7.求集合长度int size = m.size();System.out.println(size); // 2// 8.清空m.clear(); // 没有返回值}
}
- 键找值
- 键值对
- Lambda表达式
- 通过keySet()方法将所有的键都放在一个单列集合当中。
- 遍历单列集合依次得到每一个键。
- 再通过get方法获取每一个键所对应的值。
public class MapTest {public static void main(String[] args) {// 1.创建map集合对象Map m = new HashMap<>();// 2.添加元素m.put("郭靖", "穆念慈");m.put("欧阳克", "黄蓉");m.put("尹志平", "小龙女");//3.通过键找值//3.1 获取所有的键:把这些键放到一个单列集合当中Set keys = m.keySet();//3.2 遍历单列集合得到所有的键for (String key : keys) {//3.3 通过map集合当中键获取值 getString value = m.get(key);System.out.println(key + " = " + value);}}
}
- 通过entrySet()方法依次获取每一个键值对对象
- 再通过getKey() 和 getValue()方法分别获取键和值
public class MapTest {public static void main(String[] args) {// 1.创建map集合对象Map m = new HashMap<>();// 2.添加元素// 键: 人物外号// 值: 人物姓名m.put("标枪选手", "马超");m.put("人物挂件", "明世隐");m.put("御龙骑士", "花木兰");// 3.键值对// 3.1 获取所有的键值对对象 ,返回一个Set对象Set> entries = m.entrySet();for (Entry entry : entries) {// 3.2 遍历集合,获取每一个键值对对象String key = entry.getKey();String value = entry.getValue();System.out.println(key + " = " + value);}}
}
方法名称 | 说明 |
default void forEach(BiConsumer super K , ? super V> action) | 结合lambda遍历Map集合 |
public class MapTest {public static void main(String[] args) {// 1.创建map集合对象Map m = new HashMap<>();// 2.添加元素//键: 人物名字//值: 名人名言m.put("鲁迅", "这句话是我说的");m.put("曹操", "不可能绝对不可能");m.put("刘备", "接着奏乐接着舞");m.put("柯镇恶", "看我眼色行事");// 3.lambda表达式//forEach底层://利用第二种遍历方式进行遍历,依次得到每一个键和值//再利用accept方法m.forEach(new BiConsumer() {@Overridepublic void accept(String key,String value) {System.out.println(key + " = " + value);}});System.out.println("=====================");//最简lambda表达式m.forEach((key,value)->System.out.println(key + " = " + value));}
}
HashMap的特点:
- 依赖hashCode方法和equals方法保证键的唯一
- 如果键存储的是自定义对象,需要重写hashCode和equals方法。
- 如果值存储的是自定义对象,不需要重写hashCode和equals方法。
练习: Map集合案例: 统计投票人数
需求:
某个班级80名学生,现在需要组成秋游活动,班长提供了四个景点依次是(A、B、C、D) ,每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多。
public class hashMapTest {public static void main(String[] args) {// 1.定义数组存储景点String[] arr = { "A", "B", "C", "D" };// 2.利用随机数模拟80为同学投票// 并将投票结果存储起来ArrayList list = new ArrayList<>();Random r = new Random();for (int i = 0; i < 80; i++) {int index = r.nextInt(arr.length);list.add(arr[index]);}// 3.如果统计数据多 不建议使用计数器思想// 我们定义map集合 利用集合进行统计HashMap map = new HashMap<>();for (String name : list) {// 判断当前的景点在map集合当中是否存在if (map.containsKey(name)) {// 存在// 获取当前景点已经被投票次数int count = map.get(name);count++;map.put(name, count);} else {map.put(name, 1);}}System.out.println(map);// 4.求最大值int max = 0;Set> entries = map.entrySet();for (Map.Entry entry : entries) {int count = entry.getValue();if (count > max) {max = count;}}System.out.println(max);// 5. 判断哪个景点与最大值一样for (Map.Entry entry : entries) {int count = entry.getValue();if (count == max) {System.out.println(entry.getKey());}}}
}
- 由键决定: 有序、不重复、无索引。
- 这里的有序指的是保证存储和取出的顺序一致
- 原理:底层数据结构依然是哈希表,只是每个键值对元素又额外的多加了一个双链表的机制记录存储的顺序。
public class LinkedHashMapTest {public static void main(String[] args) {// 1.创建集合LinkedHashMap lhm = new LinkedHashMap<>();// 2.添加元素lhm.put("a", 123);// lhm.put("a",123 );lhm.put("a", 111);lhm.put("b", 456);lhm.put("d", 789);// 3.打印集合System.out.println(lhm); // {a=123, b=456, d=789}System.out.println(lhm); // {a=111, b=456, d=789}}
}
- TreeMap跟TreeSet底层原理一样,都是红黑树结构的。
- 由键决定特性:不重复、无索引、可排序
- 可排序:对键进行排序。
- 注意:默认按照键的从大到小进行排序,也可以自己规定键的排序规则。
代码书写的两种排序规则:
需求:
键:整数表示id
值:字符串表示商品名称
要求:按照id的升序排列、按照id的降序排列
public class TreeMapTest {public static void main(String[] args) {// 1.创建集合对象// Integer Double 默认情况按升序排列// String 按字母在ASCII 表对应数字升序排列TreeMap tm = new TreeMap<>(new Comparator() {@Overridepublic int compare(Integer i1, Integer i2) {// i1:当前要添加的元素// i2:表示已经在红黑树中存在的元素return i2 - i1;}});// 2.添加商品tm.put(2, "康师傅");tm.put(1, "粤里粤");tm.put(3, "九个核桃");tm.put(5, "可恰可乐");tm.put(4, "雷碧");// 3.打印集合// 默认按键升序排列System.out.println(tm);}
}
需求:字符串 “aababcabcdabcde"
请统计字符串中每一个字符出现的次数,并按照以下格式输出输出结果:
a (5) b (4) c (3) d (2) e ( 1)
public class TreeMapTest {public static void main(String[] args) {// 1.定义字符串String str = "aababcabcdabcde";// 2.创建集合TreeMap tm = new TreeMap<>();// 2.遍历字符串得到每一个字符for (int i = 0; i < str.length(); i++) {char c = str.charAt(i);// System.out.println(c);// 将c去集合中判断是否存在if (tm.containsKey(c)) {int count = tm.get(c);count++;tm.put(c, count);} else {tm.put(c, 1);}}// 打印集合System.out.println(tm);// 遍历集合按照指定格式进行拼接StringBuilder sb = new StringBuilder();tm.forEach((key, value) -> sb.append(key).append("(").append(value).append(")"));System.out.println(sb);StringJoiner sj = new StringJoiner("","","");tm.forEach((key,value) -> sj.add(key + "").add("(").add(value + "").add(")"));System.out.println(sj);}
}
- 可变参数本质上就是一个数组
- 作用:在形参中接收多个数据
- 格式:数据类型 ... 参数名称 举例: int ... a
- 注意事项:
- 形参列表中可变参数只能有一个
- 可变参数必须放在形参列表的最后面
案例: 可变数组的练习
假如需要定义一个方法求和,该方法可以灵活的完成如下需求:
计算2个数据的和
计算3个数据的和计算4个数据的和
计算n个数据的和
public class Test {public static void main(String[] args) {// JDK5// 可变参数:方法形参个数是可变的// 格式: 数据类型 ... 名字System.out.println(getSum(1, 2, 3)); // 6System.out.println(getSum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); // 55}// 底层:// 可变参数底层就是一个数组// 只不过不需要我们去创建,java会自动帮我们创建好public static int getSum(int... args) {// System.out.println(args); //[I@7852e922int sum = 0;for (int i : args) {sum = sum + i;}return sum;}
}
- java.util.Collections : 是集合工具类
- 作用:Collections不是集合,而是集合的工具类。
Collections的常用API:
方法名称 | 说明 |
public static | 批量添加元素 |
public static void shuffle(List> list) | 打乱List集合当中元素的顺序 |
public static | 排序 |
public static | 根据指定的规则进行排序 |
public static | 以二分查找法查找元素 |
public static | 拷贝集合中的元素 |
public static | 使用指定的元素填充集合 |
public static | 根据默认的自然排序获取最大/小值 |
public static | 交换集合中指定位置的元素 |
public class CollectionsTest {public static void main(String[] args) {// 1.创建集合对象ArrayList list = new ArrayList<>();// 2.addAll 批量添加Collections.addAll(list, "abc", "bcd", "dwer", "df", "zxcv", "1234");System.out.println(list); // [abc, bcd, dwer, df, zxcv, 1234]// 3.shuffe 打乱顺序Collections.shuffle(list);System.out.println(list); // [zxcv, abc, 1234, bcd, df, dwer]}
}