感谢黑马的 Java SE 课程。
双列集合
元素成对出现。分为键与值。二者之间是一一对应的关系。
- 双列集合一次需要存一对数据,分别为键和值
- 键不能重复,值可以重复
- 键和值是一一对应的,每一个键只能找到自己对应的值
- 键 + 值这个整体我们称之为“键值对”或者“键值对对象”,在Java中叫做“Entry对象”
Map 集合
Map 集合常见 API
Map 集合是双列集合的顶层接口。其下有 HashMap、TreeMap。
Map 集合常用方法
本章节内容摘自其它页面。【Java基础】Java中Map集合的常用方法_java map的常用方法-CSDN博客
put
如果添加的 key 已经存在,则会覆盖原来的 value 值。并返回原值。
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("a", 3);
System.out.println(map); // 输出 {a=3, b=2}get
get 方法用于获取指定 key 对应的 value 值,如果 key 不存在,则返回 null。
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
Integer value = map.get("a");
System.out.println(value); // 输出 1remove
remove 方法用于删除指定 key 对应的键值对。
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
System.out.println(map); // 输出 {a=1, b=2}
map.remove("a");
System.out.println(map); // 输出 {b=2}clear
用于清空 Map 中所有的键值对。
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
System.out.println(map); // 输出 {a=1, b=2}
map.clear();
System.out.println(map); // 输出 {}containsKey & containsValue
containsKey 和 containsValue 方法分别用于判断 Map 中是否包含指定的 key 或 value 值。
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
boolean hasA = map.containsKey("a");
boolean hasC = map.containsKey("c");
boolean hasValue2 = map.containsValue(2);
boolean hasValue3 = map.containsValue(3);
System.out.println(hasA); // 输出 true
System.out.println(hasC); // 输出 false
System.out.println(hasValue2); // 输出 true
System.out.println(hasValue3); // 输出 falsekeySet 和 values
keySet 方法返回 Map 中所有 key 的集合,values 方法返回 Map 中所有 value 的集合。
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
Set<String> keySet = map.keySet();
Collection<Integer> values = map.values();
System.out.println(keySet); // 输出 [a, b, c]
System.out.println(values); // 输出 [1, 2, 3]size 和 isEmpty
size 方法返回 Map 中键值对的数量,isEmpty 方法判断 Map 是否为空。
Map<String, Integer> map = new HashMap<>();
System.out.println(map.size()); // 输出 0
System.out.println(map.isEmpty()); // 输出 true
map.put("a", 1);
System.out.println(map.size()); // 输出 1
System.out.println(map.isEmpty()); // 输出 falseMap 集合遍历方式
1、键找值
增强 for
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class demo {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("zhangsan", "lisi");
map.put("liwu", "chenliu");
// 获取所有键并放入一个单列集合
Set<String> keys = map.keySet();
for (String key : keys) {
// 用 get 方法获取其值
String value = map.get(key);
System.out.println(key + " = " + value);
}
}
}迭代器遍历
我们同样可以使用迭代器获取每一个键的值。
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class demo {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("zhangsan", "lisi");
map.put("liwu", "chenliu");
// 获取所有键并放入一个单列集合
Set<String> keys = map.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = map.get(key);
System.out.println(key + " = " + value);
}
// for (String key : keys) {
// // 用 get 方法获取其值
// String value = map.get(key);
// System.out.println(key + " = " + value);
// }
}
}2、键值对
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class demo {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("zhangsan", "lisi");
map.put("liwu", "chenliu");
// 获取所有键并放入一个单列集合
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String,String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + " = " + value);
}
}
}Map.Entry 是 Map 接口内部定义的一个嵌套接口。它表示 Map 中的一个键值对(一个条目)。例如,对于 map.put("zhangsan", "lisi"),这个键值对就是一个 Map.Entry<String, String> 对象。
HashMap
Map 的一个实现类。
- HashMap 是 Map 里面的一个实现类。
- 没有额外需要学习的特有方法,直接使用 Map 里面的方法就可以了。
- 特点都是由键决定的:无序、不重复、无索引
- HashMap 跟 HashSet 底层原理是一模一样的,都是哈希表结构
- HashMap 底层是哈希表结构的
- 依赖 hashCode 方法和 equals 方法保证键的唯一
- 如果键存储的是自定义对象,需要重写 hashCode 和 equals 方法
- 如果值存储自定义对象,不需要重写 hashCode 和 equals 方法
LinkedHashMap
- 由键决定:有序(这里的有序指的是保证存储和取出的元素顺序一致)、不重复、无索引
- 原理:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序
可变参数
在定义方法时,在最后一个形参后加上三点 …,就表示该形参可以接受多个参数值,多个参数值被当成数组传入。
- 可变参数只能作为函数的最后一个参数,但其前面可以有也可以没有任何其他参数
- 由于可变参数必须是最后一个参数,所以一个函数最多只能有一个可变参数
- Java的可变参数,会被编译器转型为一个数组
- 变长参数在编译为字节码后,在方法签名中就是以数组形态出现的。这两个方法的签名是一致的,不能作为方法的重载。如果同时出现,是不能编译通过的。可变参数可以兼容数组,反之则不成立
以上摘自菜鸟教程。Java 可变参数 | 菜鸟教程
示例:
public class demo {
public static void main(String[] args) {
getSum(1,2,3,4,5);
}
public static int getSum(int...args) {
System.out.println(args);// 输出[I@163006a,可以判断此对象是一个数组
return 0;
}
}最后一点“变长参数在编译为字节码后,在方法签名中就是以数组形态出现的。这两个方法的签名是一致的,不能作为方法的重载。如果同时出现,是不能编译通过的。可变参数可以兼容数组,反之则不成立”,有点费解,我们拆解一下:
“变长参数在编译为字节码后,在方法签名中就是以数组形态出现的”
源码中写的 int... args,编译成 .class 字节码后,方法的参数类型会变成 int[](一维 int 数组)。
- 可变参数只是语法糖,编译器自动把多个参数包装成数组,方法内部看到的
args也就是数组。 - 因此从 JVM 的角度看,根本不存在“可变参数”这种类型,只有数组。
“这两个方法的签名是一致的,不能作为方法的重载”
public static int getSum(int... a) { return 0; }
public static int getSum(int[] b) { return 0; }编译时两者的字节码方法签名都是 getSum(int[]),完全一样。
“如果同时出现,是不能编译通过的”
void foo(int... x) {}
void foo(int[] x) {}
// 编译错误:已在类中定义了方法 foo(int[])“可变参数可以兼容数组,反之则不成立”
- 可变参数可以兼容数组:调用一个声明为
int...的方法时,你既可以传入多个int值(foo(1,2,3)),也可以直接传入一个int[]数组(foo(new int[]{1,2,3}))。后者不会报错,数组会被直接当作那个唯一的参数。 - 反之不成立:如果一个方法声明为
int[](普通数组参数),你不能直接传多个int值(foo(1,2,3)会编译错误),必须显式构造数组再传入。
public static void acceptVarargs(int... nums) {}
acceptVarargs(1,2,3); // OK
acceptVarargs(new int[5]); // OK (兼容数组)
public static void acceptArray(int[] arr) {}
acceptArray(1,2,3); // 编译错误!不能直接传多个值
acceptArray(new int[]{1,2,3}); // OK集合工具类 Collections
摘于其它页面。Java Collections:专为集合框架而生的工具类 | 二哥的Java进阶之路
排序
reverse(List list):反转顺序shuffle(List list):洗牌,将顺序打乱sort(List list):自然升序sort(List list, Comparator c):按照自定义的比较器排序swap(List list, int i, int j):将 i 和 j 位置的元素交换位置
示例:
List<String> list = new ArrayList<>();
list.add("沉默王二");
list.add("沉默王三");
list.add("沉默王四");
list.add("沉默王五");
list.add("沉默王六");
System.out.println("原始顺序:" + list);
// 反转
Collections.reverse(list);
System.out.println("反转后:" + list);
// 洗牌
Collections.shuffle(list);
System.out.println("洗牌后:" + list);
// 自然升序
Collections.sort(list);
System.out.println("自然升序后:" + list);
// 交换
Collections.swap(list, 2,4);
System.out.println("交换后:" + list);输出:
原始顺序:[沉默王二, 沉默王三, 沉默王四, 沉默王五, 沉默王六]
反转后:[沉默王六, 沉默王五, 沉默王四, 沉默王三, 沉默王二]
洗牌后:[沉默王五, 沉默王二, 沉默王六, 沉默王三, 沉默王四]
自然升序后:[沉默王三, 沉默王二, 沉默王五, 沉默王六, 沉默王四]
交换后:[沉默王三, 沉默王二, 沉默王四, 沉默王六, 沉默王五]查找
binarySearch(List list, Object key):二分查找法,前提是 List 已经排序过了max(Collection coll):返回最大元素max(Collection coll, Comparator comp):根据自定义比较器,返回最大元素min(Collection coll):返回最小元素min(Collection coll, Comparator comp):根据自定义比较器,返回最小元素fill(List list, Object obj):使用指定对象填充frequency(Collection c, Object o):返回指定对象出现的次数
System.out.println("最大元素:" + Collections.max(list));
System.out.println("最小元素:" + Collections.min(list));
System.out.println("出现的次数:" + Collections.frequency(list, "沉默王二"));
// 没有排序直接调用二分查找,结果是不确定的
System.out.println("排序前的二分查找结果:" + Collections.binarySearch(list, "沉默王二"));
Collections.sort(list);
// 排序后,查找结果和预期一致
System.out.println("排序后的二分查找结果:" + Collections.binarySearch(list, "沉默王二"));
Collections.fill(list, "沉默王八");
System.out.println("填充后的结果:" + list);输出:
原始顺序:[沉默王二, 沉默王三, 沉默王四, 沉默王五, 沉默王六]
最大元素:沉默王四
最小元素:沉默王三
出现的次数:1
排序前的二分查找结果:0
排序后的二分查找结果:1
填充后的结果:[沉默王八, 沉默王八, 沉默王八, 沉默王八, 沉默王八]
Comments NOTHING