-
目录
-
1.不可变集合
- 1.1不可变集合定义
- 是一个长度不可变,内容也无法修改的集合
- 1.2使用场景
- 如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。
- 当集合对象被不可信的库调用时,不可变形式是安全的。
- 简单理解:不想让别人修改集合中的内容
- 比如说:
- 1,斗地主的54张牌,是不能添加,不能删除,不能修改的
- 2,斗地主的打牌规则:单张,对子,三张,顺子等,也是不能修改的
- 3,用代码获取的操作系统硬件信息,也是不能被修改的
- 1.1不可变集合定义
-
2.不可变集合的分类
- 2.1不可变的list集合
List<String> list = List.of("张三", "李四", "王五", "赵六"); //只能进行查询操作
- 2.2不可变的set集合
Set<String> set = Set.of("张三", "张三", "李四", "王五", "赵六"); //参数一定要保证唯一性且只能进行查询操作
- 2.3不可变的map集合
Map<String, String> map = Map.of()- 细节1:键是不能重复的
- 细节2:Map里面的of方法,参数是有上限的,最多只能传递20个参数,10个键值对
- 细节3:如果我们要传递多个键值对对象,数量大于10个,在Map接口中还有一个方法:
Map map = Map.ofEntries(arr2);- 步骤1:创建一个普通的Map集合
HashMap<String, String> hm = new HashMap<>();
- 步骤2:
- 1.获取到所有的键值对对象(Entry对象)
Set<Map.Entry<String, String>> entries = hm.entrySet();
- 2.把entries变成一个数组
Map.Entry[] arr1 = new Map.Entry[0];
- 3.toArray方法在底层会比较集合的长度跟数组的长度两者的大小
Map.Entry[] arr2 = entries.toArray(arr1);- 集合的长度 > 数组的长度 :数据在数组中放不下,此时会根据实际数据的个数,重新创建数组
- 集合的长度 <= 数组的长度:数据在数组中放的下,此时不会创建新的数组,而是直接用
- 4.不可变的map集合
Map map = Map.ofEntries(arr2);
- 1.获取到所有的键值对对象(Entry对象)
- 简写:Map<Object, Object> map = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));
- 简写Plus:Map<String, String> map = Map.copyOf(hm);
注意:此方法是在JDK10才出现
- 步骤1:创建一个普通的Map集合
- 2.1不可变的list集合
-
3. Stream流
- 3.1Stream流的思想
- 3.2Stream流的三类方法
- 获取Stream流
例如:List<String> list = new ArrayList<String>();- 创建一条流水线,并把数据放到流水线上准备进行操作
- 中间方法
- 流水线上的操作
- 一次操作完毕之后,还可以继续进行其他操作
- 终结方法
- 一个Stream流只能有一个终结方法
- 是流水线上的最后一个操作
- 获取Stream流
- 3.3生成Stream流的方式
- Collection体系集合:使用默认方法stream()生成流, default Stream<E> stream()
- List<String> list = new ArrayList<String>();
- Stream<String> listStream = list.stream();
- Map体系集合:把Map转成Set集合,间接的生成流
- 方式一:
- Map<String,Integer> map = new HashMap<String, Integer>();
- Stream<String> keyStream = map.keySet().stream();
- Stream<Integer> valueStream = map.values().stream();
- 方式二:
- Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
- 方式一:
- 数组:通过Arrays中的静态方法stream生成流
- String[] strArray = {"hello","world","java"};
- Stream<String> strArrayStream = Arrays.stream(strArray);
- 同种数据类型的多个数据:通过Stream接口的静态方法of(T... values)生成流
- Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
- Stream<Integer> intStream = Stream.of(10, 20, 30);
- Collection体系集合:使用默认方法stream()生成流, default Stream<E> stream()
- 3.1Stream流的思想
-
4. Stream流的操作方法
- 注意:Stream只能使用一次,所以没有必要用变量进行接收(使用链式编程)
- 4.1中间操作
概念:中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作- Stream<T> filter(Predicate predicate)
用于对流中的数据进行过滤,建议使用链式编程- list.stream().filter(
- new Predicate<String>() {
- @Override
- public boolean test(String s) {
- boolean result = s.startsWith("张");
- return result;//结果为true,则当前的数据留
- }
- }
- ).forEach(s-> System.out.println(s));
- Stream<T> limit(long maxSize)
返回此流中的元素组成的流,截取前指定参数个数的数据
- Stream<T> skip(long n)
跳过指定参数个数的数据,返回由该流的剩余元素组成的流
- Stream<T> distinct()
返回由该流的不同元素(根据Object.equals(Object) ,所以要重写hashcode和equals)组成的流
- static <T> Stream<T> concat(Stream a, Stream b)
合并a和b两个流为一个流,如果a,b类型不同,则合并之后是a与b共同的父类ab(类型的提升,但是会无法使用子类的特有功能)- 注意static:直接调用,Stream.concat(list1. stream(),list. stream())
- Stream<R> map(Function<T,R> mapper)
转换流中的数据
T为转换类型,R为目标类型,利用函数式接口Function中的方法public R apply(T var1)
- Stream<T> filter(Predicate predicate)
- 4.2终结操作
概念:终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作- void forEach(Consumer action)
对此流的每个元素执行操作,接口中是accept方法
- long count()
返回此流中的元素数
- void forEach(Consumer action)
- 4.3收集操作
概念:对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中- 数组:A[] / Object[] toArray( IntFunction<A[]> generator / 空参)
收集流中的数据,放到数组中
注意IntFunction<A[]> generator:这里A[]是一个具体类型的数组(例如String类型:new IntFunction<String[]>())
里面是accept方法:public A[] apply(int value){ return new A[value] } 1.其中A与上相同,2.value是流中数据的个数,要和数组保持一致,3.返回值与上相同- 总结:
- toArray方法的参数:负责创建一个指定类型的数组
- toArray方法的底层:会一次得到流里面的每一个数据,并把数据放在数组中
- toArray方法的返回值:是一个装着流里面所有数据的数组
- 例如:list. stream().toArray(value -> new String[value]);
- 单列集合:
- R collect(Collector collector)
把结果收集到集合中,与其他收集集合进行配合(List,Set,Map)
- 工具类Collectors提供了具体的收集方式:
- public static <T> Collector toList()
把元素收集到List集合中
- public static <T> Collector toSet()
把元素收集到Set集合中
- 例如:List<Integer> list = list1. stream( ).filter( ).collect(Collectors.toList());
- R collect(Collector collector)
- 双列集合:
注意:如果我们要收集到Map集合当中,键不能重复,否则会报错- public static Collector toMap(Function<K,V> keyMapper,Function<K,V> valueMapper)
把元素收集到Map集合中
- public static Collector toMap(Function<K,V> keyMapper,Function<K,V> valueMapper)
- 数组:A[] / Object[] toArray( IntFunction<A[]> generator / 空参)
-
5.方法引用
- 把已经有的方法拿过来用,当作函数式接口中抽象方法的方法体
相当于传递的不是实现类或者子类,而是类或者接口中已经存在的方法 - 5.1方法引用的条件
- 1.引用处必须是函数式接口
Arrays.sort(arr , new Comparator<Integer> { });
- 2.被引用的方法必须已经存在
- 3.被引用的方法的形参和返回值需要跟抽象方法保持一致
- 4.被引用方法的功能要满足当前的需求
- 1.引用处必须是函数式接口
- 5.2引用静态方法
- 格式:类名 : : 静态方法
Integer : : parseInt
Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据
- 格式:类名 : : 静态方法
- 5.3引用成员方法
- 格式:对象 : : 成员方法
- ①其他类:其它类对象 : : 方法名
- ②本类:this : : 方法名
注意:引用处不能是静态方法
- ③父类:super : : 方法名
注意:引用处不能是静态方法
- 格式:对象 : : 成员方法
- 5.4引用构造方法
- 格式:类名 : : new
- 注意:Ⅰ.重写抽象方法之后的方法体里面返回值为new 类名(参数1,参数2...) ,则可使用引用方法。Ⅱ.类中的构造方法按照需求进行补充,引用构造方法的时候,对象便已经存在了,所以返回值不需要去管
- 5.5其他调用方式-类名引用成员方法
- 格式:类名 : : 成员方法
- 独有的引用规则:
- 1.引用处必须是函数式接口
Arrays.sort(arr , new Comparator<Integer> { });
- 2.被引用的方法必须已经存在
- 3.被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致
- 4.被引用方法的功能要满足当前的需求
- 1.引用处必须是函数式接口
- 抽象方法形参的详解
- 第一个参数:Ⅰ表示被引用方法的调用者,决定了可以引用哪些类中的方法。Ⅱ在Stream流中,第一个参数一般都表示流里面的每一个数据。Ⅲ假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法
- 第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法
- 局限性
- 不能引用所有类中的成员方法。是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法。
例如:list. stream().map(String::toUpperCase)
map中参数的实现类方法:public String apply(String s) { return s.toUpperCase; }
- 不能引用所有类中的成员方法。是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法。
- 5.6其他调用方式-引用数组的构造方法
- 格式:数据类型 [] : : new
int [ ] : : new
就是为创建数组
- 注意:数组的类型,需要跟流中数据的类型保持一致
Integer[] arrr = list. stream().toArray(Integer[] : : new);
- 格式:数据类型 [] : : new
- 5.7方法调用小技巧
- 1. 现在有没有一个方法符合我当前的需求
- 2. 如果有这样的方法,这个方法是否满足引用的规则
- 静态 类名 : : 静态方法
- 成员方法 类名 : : 成员方法/对象 : : 成员方法
- 构造方法 类名 : : 成员方法
- 把已经有的方法拿过来用,当作函数式接口中抽象方法的方法体