不可变集合(Set、List、Map)
什么是不可变集合?
-
不可变集合,就是不可被修改的集合
-
集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变,否则报错!
举个例子:网页上的一些敏感数据并不想让用户进行修改就采用不可变集合~
为什么要创建不可变集合?
- 如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。
- 或者当集合对象被不可信的库调用时,不可变形式是安全的。
如何创建不可变集合?
- 在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合。
方法名称 | 说明 |
---|---|
static List of(E…elements) | 创建一个具有指定元素的List集合对象 |
static Set of(E…elements) | 创建一个具有指定元素的Set集合对象 |
static<K,V> Map<K,V> of(E…elements) | 创建一个具有指定元素的Map集合对象 |
注意:该方法在jdk1.9后才有! 可通过cmd输入 java -version 查看
Stream流
废话不多说,直接上案例!(案例来自:黑马程序员)
需求:按照下面的要求完成集合的创建和遍历
-
创建一个集合,存储多个字符串元素
List<String> list = new ArrayList<>(); list.add("张无忌"); list.add("周芷若"); list.add("赵敏"); list.add("张强"); list.add("张三丰");
-
把集合中所有以“张”开头的元素存储到一个新的集合
-
把“张”开头的集合中的长度为3的元素存储到一个新的集合
-
遍历上一步得到的集合中的元素输出
代码展示 -->
普通方法
public class StreamTest {
public static void main(String[] args) {
// 创建数组并填入数据
List<String> names = new ArrayList<>();
Collections.addAll(names , "张三丰" , "张无忌" , "周芷若" , "赵敏" , "张强");
System.out.println(names);
// 1、从集合中找出姓张的放到新集合
List<String> zhangList = new ArrayList<>();
for (String name : names) {
if (name.startsWith("张")){
zhangList.add(name);
}
}
System.out.println(zhangList);
// 2、找名称长度是3的姓名
List<String> zhangThreeList = new ArrayList<>();
for (String name : zhangList) {
if (name.length() == 3){
zhangThreeList.add(name);
}
}
System.out.println(zhangThreeList);
}
}
这代码量实在太多了…
使用Stream实现
public class StreamTest {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
Collections.addAll(names , "张三丰" , "张无忌" , "周芷若" , "赵敏" , "张强");
System.out.println(names);
/* 使用Stream实现 */
names.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s)); // 一行代码直接实现!
}
}
可见,集合和数组的API在Stream面前简直就是个“弟弟”!
ps:Stream支持链式哦~
深度解析:
1、names.stream() --> 调用stream方法
2、filter() --> 过滤
3、startsWith() --> 集合中的元素以什么开始(在括号中填入条件)
4、forEach --> 遍历
5、其实Stream流顾名思义,就是一条小溪(即创建的集合数组),这条小溪携带着各种泥沙和碎石(即往集合数组中填入数据)。这条小溪在不断前进的过程中因水流的速度而导致了质量重的砂砾沉底,而质量较轻的砂砾继续随着水流前行(即根据你所设定的条件不断在集合数组中进行筛选 --> filter)。【个人想法个人想法个人想法!!如有不恰当请即使指出~】
Stream流补充:
一、获取流
Collection集合获取流、Map集合获取流、数组获取流
/* Collection集合获取流 */
Collection<String> list = new ArrayList<>();
Stream<String> s = list.stream();
/* Map集合获取流 */
// map的元素是一个整体(键和值),它默认这一个整体没有类型,所以没办法直接拿Stream流
Map<String , Integer> maps = new HashMap<>();
// 键流 -> 先获得键后获取流
Stream<String> keyStream = maps.keySet().stream();
// 值流 -> 先获得值后获取流
Stream<Integer> valueStream = maps.values().stream();
// 键值对流(拿整体) -> 先转为Set后获取流
Stream<Map.Entry<String, Integer>> keyAndValueStream = maps.entrySet().stream();
/* 数组获取流 */
// 两种方法
String[] names = {"赵敏" , "小昭" , "灭绝" , "周芷若"};
// 1、public static <T> Stream<T> stream(T[] array) --> 获取当前数据的Stream流
Stream<String> nameStream1 = Arrays.stream(names);
// 2、public static <T> Stream<T> of(T... values) --> 获取当前数据/可变数据的Stream流
Stream<String> nameStream2 = Stream.of(names);
二、Stream流的常用API(中间操作方法)
名称 | 说明 |
---|---|
Stream filter(Predicate<? super T> predicate) | 用于对流中的数据进行过滤 |
Stream limit(long maxSize) | 获取前几个元素 |
Stream skip(long n) | 跳过前几个元素 |
Stream distinct() | 去除流中重复的元素 |
static Stream concat(Stream a , Stream b) | 合并a和b两个流为一个流 |
注意:中间操作方法即调用完成后返回新的Stream流可以继续使用,简单点来说就是支持链式编程!也称作非终结方法!
终结操作方法
名称 | 说明 |
---|---|
void forEach(Consumer action) | 对此流的每个元素执行遍历操作 |
long count() | 返回此流中的元素数 |
上代码!
/**
Stream流的常用API
forEach : 逐一处理(遍历)
count : 统计个数
-- long count();
filter : 过滤元素
-- Stream<T> filter(Predicate<? super T> predicate)
limit : 取前几个元素
skip : 跳过前几个
map : 加工方法
concat : 合并流
*/
public class StreamDemo02 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Collections.addAll(list , "张无忌" , "周芷若" , "赵敏" , "张强" , "张三丰" , "张三丰");
/* Stream<T> filter(Predicate<? super T> predicate)*/
list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));
/* count : 统计个数 --> long count()*/
long size = list.stream().filter(s -> s.length() == 3).count();
System.out.println(size);
/* limit : 取前几个元素*/
list.stream().limit(3).forEach(s -> System.out.println(s));
// list.stream().limit(3).forEach(System.out::println); --> 基于上一行代码的简化
/* skip : 跳过前几个*/
list.stream().skip(3).forEach(System.out::println);
/* map : 加工方法:第一个参数原材料 -> 第二个参数是加工后的结果*/
// 需求:给集合元素的前面都加上一个: Rocky的 :
list.stream().map(s -> "Rocky的 :" + s).forEach(System.out::println);
// 需求:把所有的名称 都加工成一个学生对象
list.stream().map(s -> new Student(s)).forEach(System.out::println); // 要先创建个Student对象
/* concat : 合并流*/
Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
Stream<String> s2 = Stream.of("Java1" , "Java2");
Stream<String> s3 = Stream.concat(s1 , s2);
s3.forEach(System.out::println);
}
}
三、Stream流的收集操作
- 收集Stream流的含义:就是把Stream流操作后的结果数据转回到集合或者数组中去。
- Stream流:方便操作集合/数组的手段。
- 集合/数组:才是开发中的目的。
Stream流的收集方法
名称 | 说明 |
---|---|
R collect(Collector collector) | 开始收集Stream流,指定收集器 |
Collectors工具类提供了具体的收集方式
名称 | 说明 |
---|---|
public static Collector toList() | 把元素收集到List集合中 |
public static Collector toSet() | 把元素收集到Set集合中 |
public static Collector toMap(Function keyMapper , Function valueMapper) | 把元素收集到Map集合中 |
List<String> list = new ArrayList<>();
Collections.addAll(list , "张无忌" , "周芷若" , "赵敏" , "张强" , "张三丰" , "张三丰");
/* (1)*/
Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
List<String> zhangList = s1.collect(Collectors.toList());
System.out.println(zhangList);
/* (2)*/
// 运行下面两行代码时一定要把上面 (1)list的收集方法(三行代码)给注释掉!!!
// 因为流只能运行一次,除非像 (3) 重新创建一个流!否则报错!!!
Set<String> zhangSet = s1.collect(Collectors.toSet());
System.out.println(zhangSet); // Set会去重,所以只有三个元素
/* (3)*/
Stream<String> s2 = list.stream().filter(s -> s.startsWith("张"));
Object[] arr = s2.toArray();
System.out.println(Arrays.toString(arr));
ps : 文章内容仅个人学习后的自我总结,如有错误和不足望大家们及时提出!
动手实践才是真谛,快去动手试试吧!