Java 集合进阶使用(List Map Set 异常)
Collection

Collection 是其子集的父类,所以可以使用多态的规矩,比如:创建一个 ArrayList 对象,用 Collection 接收
Collection<Integer> collection = new ArrayList<>();
注意:Collection 为接口,不能直接创建对象,但可以利用其子类,使用 Collection 方法,就如上方代码一样
Collection 方法基本使用
Collection<Integer> collection = new ArrayList<>();
collection.add(1); // 添加一个数据
ollection.add(2);
System.out.println(collection); // [1, 2]
collection.clear(); // 清空集合中的所有值
System.out.println(collection); // []
collection.add(1);
collection.add(2);
collection.remove(2); // 删除值为 2 的数据
System.out.println(collection); // [1]
boolean contains = collection.contains(1); // 是否包含值为 1 的数据
System.out.println(contains); // true
boolean empty = collection.isEmpty(); // 验证是否为空
System.out.println(empty); // false
int size = collection.size(); // 集合长度
System.out.println(size); // 1
注意:
add方法是有一个返回值为boolean,List系列始终为true,Set系列由于不能添加重复数据,可能为falsecontains方法如果查询是否包含某个对象,比如Student类的对象,contains默认对比的是地址值,若要对比的是数据,需要重写Student类的equals方法
Collection 遍历
Intrator 迭代器遍历
可以把 Intrator 看作一个箭头,默认指向第 0 索引,调用一次 next(),箭头就会向后移动一位。
- 如果后一位没有值,还要强行
next()访问,就会报异常 hasNext()方法,判断是否有下一个元素值- 迭代器遍历期间,不能使用添加删除等改变集合的方法
Collection<String> collection = new ArrayList<>();
collection.add("a");
collection.add("b");
collection.add("c");
collection.add("d");
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next()); // a
System.out.println(iterator.next()); // b
System.out.println(iterator.next()); // c
System.out.println(iterator.next()); // d
System.out.println(iterator.next()); // 如果 next 已经没有值,强行访问会报异常 NoSuchElementException
}
增强 for 遍历
List 系列和数组可以使用,快捷键 对象名.for

Lambda 表达式遍历
和 js 的 forEach 相似,不过 java 中的 forEach 没有第二个参数 i
collection.forEach(v->{
System.out.println(v);
});
List
Collection 是 List 的父类接口,而 List 又是 ArrayList 的父类接口,其用法和 Collection 差不多,
迭代器,增强 for,lambda 都可以用
LikedList
双向链表:查询慢,守卫操作增删改的速度极快

泛型
基本写法
和 TS 中的泛型类似
泛型类

泛型方法

泛型接口:和泛型类的用法相似

泛型通配符和泛型类型约束
?表示不确定的类型,可以不传泛型

extends E表示可以传入的泛型为 E 类以及基于 E 的子类

super E表示可以传入的泛型为 E 类以及其所有的父类,和extends刚好相反
Set
Set 不能使用 for 循环遍历,无序也就不能通过索引获取元素
Set 的 API 和 Collection 基本保持一致
- HashSet:无序、不重复、无索引
- LinkedHashSet:有序、不重复、无索引
- TreeSet:可排序、不重复、无索引
HashSet
HashSet 基本使用

由图上结果可知,重复的对象并未被添加到 HashSet 中,因为重写了 hashCode 方法,两个相同对象值拥有相同的 hashCode,如果不重写 hashCode,对象值一样也不会重复。

LinkedHashSet
当需要存有序数据的时候用 LinkedHashSet
TreeSet
对于数值类型:整数和小数,默认都采用从小到大排序
对于字符和字符串:默认采用 ASCII 码表中的字母代表的数字进行排序
复杂类型排序
比如对 Person 类创建的对象排序,如果直接向 TreeSet 中添加对象,则会报错,因为 TreeSet 无法识别对象的默认排序规则

解决方法一:对指定的 javabean 类实现 Comparable 接口,制定排序规则

解决方法二:在创建 TreeSet 对象的时候直接通过 Comparator 指定排序规则

当然,该代码也可以简写为 Lambda 格式

应用场景中,
ArrayList使用最多,涉及到去重,HashSet使用最多
Map
Map 是一个接口,所以也不能直接 new 创建 Map 对象,需要利用多态,间接使用 Map 的方法
Map 的使用和 js 中的 map 相似

Map<String, String> map = new HashMap<>();
map.put("张三", "男");
map.put("李四", "女");
map.put("王五", "人妖");
System.out.println("put: " + map); // {李四=女, 张三=男, 王五=人妖}
map.remove("王五");
System.out.println("remove: " + map); // {李四=女, 张三=男}
map.clear();
System.out.println("clear: " + map); // {}
map.put("张三", "男");
map.put("李四", "女");
map.put("王五", "人妖");
boolean containsKey = map.containsKey("张"); // containsKey 是精确匹配
System.out.println("containsKey: " + containsKey); //false
boolean containsValue = map.containsValue("男");
System.out.println("containsValue: " + containsValue); // true
boolean empty = map.isEmpty();
System.out.println("isEmpty: " + empty); // false
int size = map.size();
System.out.println("size: " + size); // 3
Map的get方法就是根据键获取对应的值
Map 遍历
keySet
通过 keySet 方法获取到 Map 对象的键的集合,类似 js 中的 Object.keys,然后可以通过键找值
Map<String, String> map = new HashMap<>();
map.put("张三", "男");
map.put("李四", "女");
map.put("王五", "人妖");
Set<String> strings = map.keySet();
strings.forEach(s -> System.out.println(s)); // 循环三次,分别打印 李四 张三 王五
entrySet
entrySet 会生成一个键值对数组(Entry 数组),然后遍历数组,从每一个键值对(Entry)上调用 getKey 方法获取键,getValue 方法获取值
Map<String, String> map = new HashMap<>();
map.put("张三", "男");
map.put("李四", "女");
map.put("王五", "人妖");
Set<Map.Entry<String, String>> entries = map.entrySet();
entries.forEach(entry -> {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
// 李四=女
// 张三=男
// 王五=人妖
});
BiConsumer和Lambda简写

# HashMap
HashMap 是基于 Map 的实现类,和 Map 的用法一样。
依赖 hashCode 和 equals 保证 键的唯一
键如果是自定义对象,需要重写 hashCode 和 equals 来保证唯一的键
LinkedHashMap
与 Map 不同的是:存储的 Map 是是有序的
- 正常存储
Map是无序的 LinkedHashMap就是在Map的基础上增加了有序的功能
TreeMap
和 TreeSet 用法基本一致
可变参数
可变参数本质就是一个数组,可以在形参中接收多个不固定参数,和 js 中的 args 是一个效果

Stream 流写法
在 js 中,filter 过滤数组是可以直接使用的,但是 java 中必须使用 stream 流才行

Stream 流中的方法

Stream 流中修改数据,均不会影响到原有数据


可以将 toArray 理解成,将Stream 流转换成集合数组。
也可以使用 collect 直接收集转成数组集合,使用上比 toArray 更方便

方法引用
类似的写法在js 中也有,不同的是
- js 中,可以直接使用函数,参数可以自动传入
- java 中,必须通过
类名::方法的写法来使用方法引用

异常
try catch 和 throw,try catch 是异常捕获,throw 是主动抛出异常
try catch捕获到的异常,会走进catch中,且之后的代码不会执行catch中若只打印异常e.printStackTrace();,是不会影响后续代码的运行- 异常中
Exception是范围最广的异常,所有异常都可以用Exception捕获 - 如果没有进行
try catch却发生了异常,则交给 jvm 自动处理该异常
自定义异常
常见的异常分为 RuntimeException 运行时异常 和 Exception 编译时异常
- 继承异常类
RuntimeException或者Exception或者其它异常类 alt+insert对父类的构造方法进行重写(根据业务进行重写构造,也可以直接使用父类构造)
// 最简单的自定义异常
public class MyException extends RuntimeException{
public MyException() {
}
public MyException(String message) {
super(message);
}
}