JAVA学习 API_day11(属性集, 函数式编程, stream流)

属性集, 函数式编程, stream流

I/O流小结

文件复制:
BufferedInputStream/BufferedOutputStream
文件内容读写:
BufferedReader/PrintWriter
FileReader/FileWriter
对象读写:
ObjectInputStream/ObjectOutputStream

1. 属性集

Properties, 仅支持String类型的属性映射
extends Hashtable implements Map
key - value,
推荐使用的方法
void setProperty(String key, String value)
String getProperty(String key)
加载属性集:
void load(Reader)
void load(InputStream)
代码实现:

public class PropertiesTest {
    @Test
    //属性集创建
    //属性列表为HashTable的子类
// Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。属性列表中的每个键及其对应的值都是一个字符串
    public void propertiesDefine(){
        //构造一个新的属性集
        Properties properties = new Properties();
        //不要使用get/put方法因为可以输入Object对象
        //添加键值对
        //键值一般用英文
        properties.setProperty("价格", "278");//参数只支持String类型
        properties.setProperty("评分", "特别好评");
        System.out.println(properties);
        //获取键对应的值
        System.out.println( properties.get("价格"));
        System.out.println( properties.get("评分"));
        //获取键值列表
        Set<String> key = properties.stringPropertyNames();
        //键值遍历
        for(String k: key) {
            System.out.println(k + "=" + properties.get(k));
        }
    }

    @Test
    public void read(){
        //构造一个新的属性集
        Properties properties = new Properties();
        //通过流加载属性集
        //在与类的同一个包中找目标文件
        String path = PropertiesTest.class.getResource("config.properties").getPath();
        System.out.println(path);
        try(
           FileInputStream fis = new FileInputStream(path);
           BufferedReader br = new BufferedReader(new InputStreamReader(fis, "gbk"))
        ){//load(InputStream/Reader)
            properties.load(fis);//从输入字节流读取属性列表(键和元素对)。
            properties.load(br);//以简单的线性格式从输入字符流读取属性列表(关键字和元素对)。
            //由于我的文件为gbk编码的,所以默认的ISO8859-1解码方式会生成乱码
            //如果用中文
            System.out.println(properties.getProperty("name"));
            //解决乱码
            String name = new String(properties.getProperty("name").getBytes("ISO8859-1"), "gbk");
            System.out.println(name);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    @Test
    public void write(){
        //构造一个新的属性集
        Properties properties = new Properties();
        //键值一般用英文
        properties.setProperty("name", "怪物猎人世界");
        properties.setProperty("price", "278");//参数只支持String类型
        properties.setProperty("review", "特别好评");
        //通过流打印属性集
        //在与类的同一个包中找目标文件
        String path = PropertiesTest.class.getResource("config.properties").getPath();
        System.out.println(path);
        try(
               PrintWriter pw = new PrintWriter(path, "gbk")
        ){//list(PrintWriter/PrintStream)
            properties.list(pw);//将此属性列表打印到指定的输出流。
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

2. 函数式编程

JDK 8 特性
函数式编程: Lambda表达式(函数式接口作为方法的参数)
函数式接口: 接口中只有一个抽象方法
常用函数式接口: Supplier Consumer Predicate Function
Supplier: 生产者 - T get();

import java.util.Scanner;
import java.util.function.Supplier;

public class SupplierTest {
    public static void main(String[] args) {
        Scanner console = new Scanner(System.in);
        //Supplier是一个函数式接口,因此可以用作lambda表达式或方法引用的赋值对象。

        String s = change(new Supplier<String>(){

            @Override
            public String get() {
                return console.nextLine();
            }
        });
        System.out.println(s);
        //lambda简化
        s = change(() -> console.nextLine());
        System.out.println(s);
    }
    public static String change(Supplier<String> s){
        //生产者的意义就是生产对象
        //使用get方法生产对象,返回值为Supplier的泛型
        return s.get();
    };
}

Consumer: 消费者 - void accept(T t); 使用这个对象
默认方法 - andThen(Consumer)
将两个消费方式组合在一块

import java.util.Scanner;
import java.util.function.Consumer;

public class ConsumerTest {
    public static void main(String[] args) {
        Scanner console = new Scanner(System.in);
        String s = console.nextLine();
        toCase(s, new Consumer<String>() {
            @Override
            //消费一个参数
            public void accept(String s) {
                System.out.println(s.toLowerCase());//全部小写
            }
        }, new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s.toUpperCase());//全部大写
            }
        });
        //lambda简化
        toCase(s, (s1) -> System.out.println(s.toLowerCase()), (s2) -> {
            System.out.println(s.toUpperCase());
        });
    }
    //消费一个数据,其数据类型由泛型决定, 返回void。
    public static void toCase(String s, Consumer<String> c, Consumer<String> a){
        //抽象方法accept,返回值为void
        c.accept(s);
        a.accept(s);
        //可以将上面两步使用默认方法andThen连接,效果相同,返回值为void
        c.andThen(a).accept(s);
    }
}

Predicate: 对对象做判断 - boolean test(T t);
默认方法 - or(||) and(&&) negate(!)

import java.util.ArrayList;
import java.util.function.Predicate;

public class PredicateTest {
    //predicate接口:进行数据的判断
    //1.接口抽象方法 boolean test(T t) 。用于条件判断的场景:
    public static  boolean judge(String s,Predicate<String> one){
        return one.test(s);
    }
    //2.接口默认方法 and(predicate) 将多次test方法的结果相与,返回boolean结果
    public static  boolean judgeAnd(String s,Predicate<String> one, Predicate<String> two){
        return one.and(two).test(s);
        //等同于:one.test(s) && two.test(s)
    }
    //2.接口默认方法 or(predicate) 将多次test方法的结果相与,返回boolean结果
    public static  boolean judgeOr(String s,Predicate<String> one, Predicate<String> two){
        return one.or(two).test(s);
        //等同于:one.test(s) || two.test(s)
    }
    //3.默认接口方法 negate() 取反
    public static  boolean judgeNegate(String s,Predicate<String> one){
        return one.negate().test(s);
        //等同于:!one.test(s)
    }

    public static void main(String[] args) {
        //案例:数组当中有多条“姓名+性别”的信息如下,请通过 Predicate 接口的拼装将符合要求的字符串筛选到集合 ArrayList 中,需要同时满足两个条件:
        //1. 必须为pc平台;
        //2. 名字为4个字。
        String[] array = new String[]{"怪物猎人,pc,278", "塞尔达传说,switch,315", "黎明杀机,pc,67", "女神异闻录5,ps4,199",
        "光环,Xbox,250"};
        //创建一个ArrayList表
        ArrayList<String> list = new ArrayList<>();
        //使用Predicate接口的and方法满足上述条件
        for(String s : array){
            //将信息分离
            String[] info = s.split(",");
            //直接使用lambda简化函数式接口参数
            boolean b = judgeAnd(s,s1 ->  info[1].equals("pc"),//1. 必须为pc平台;
                    s2 -> info[0].length() == 4); //2. 名字为4个字。
            if(b){//如果满足条件就添加进列表
                list.add(s);
            }
        }
        System.out.println(list);
        list.clear();
        //案例:只要价格低于100 或者 平台不是pc的就加入列表
        //使用Predicate接口的or和negate方法满足上述条件
        for(String s : array){
            //将信息分离
            String[] info = s.split(",");
            //直接使用lambda简化函数式接口参数
            boolean b = judgeOr(s,s1 ->  Integer.parseInt(info[2]) < 100,//1. 价格低于100
                    s2 -> judgeNegate(info[1], (s3) -> s3.equals("pc"))); //2. 平台不是pc
            if(b){//如果满足条件就添加进列表
                list.add(s);
            }
        }
        System.out.println(list);
    }
}

Function<T, R>: 类型转换 - R apply(T t);
默认方法 - andThen(Function)
连续做两种类型转换

import java.util.function.Function;

public class FunctionTest {
    //Function<T, R>接口用来根据一个类型的数据得到另一个类型的数据 T:输入类型 R:结果类型
    //apply方法用于类型转换
    public static Integer FunctionApply(String s, Function<String, Integer> one){
        return one.apply(s);
    }
    //andThen(after)方法返回一个组合函数,首先将此函数应用于其输入,然后将after函数应用于结果。
    public static String FunctionAndThen(String s, Function<String, Integer> one, Function<Integer, Integer> two, Function<Integer, String> three){
        return  one.andThen(two).andThen(three).apply(s);
        //相当于:three.apply(two.apply(one.apply(s)));
    }

    public static void main(String[] args) {
        String s = "怪物猎人";
        String change = FunctionAndThen(s, s1 -> s1.length(), i -> i * 100, i1 -> "价格为" + i1 + "元");
        System.out.println(change);
    }
}

3. stream流

当需要对多个元素进⾏操作(特别是多步操作)的时候,考虑到性能及便利性,我们应该⾸先拼好⼀个“模型”步骤⽅案,然后再按照⽅案去执⾏它。在这里插入图片描述
这张图中展示了过滤、映射、跳过、计数等多步操作,这是⼀种集合元素的处理⽅案,⽽⽅案就是⼀种“函数模型”。图中的每⼀个⽅框都是⼀个“流”,调⽤指定的⽅法,可以从⼀个流模型转换为另⼀个流模型。⽽最右侧的数字3是最终结果。
这⾥的 filter 、 map 、 skip 都是在对函数模型进⾏操作,集合元素并没有真正被处理。只有当终结⽅法 count 执⾏的时候,整个模型才会按照指定策略执⾏操作。⽽这得益于Lambda的延迟执⾏特性。

备注:“Stream流”其实是⼀个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值)。

Stream流: 操作 数组或者集合
获取流: 1.集合 Collection Map 2.数组

import java.util.*;
import java.util.stream.Stream;

public class GetStream {
    //获取流.stream()方法
    public static void main(String[] args) {
        //1.单列集合获取流
        List<String> list = new ArrayList<>();
        Stream<String> listStream = list.stream();
        Set<String> set = new HashSet<>();
        Stream<String> setStream = set.stream();
        //2.多列集合获取流(使用方法获取单列集合,在通过单列集合获取流)
        Map<String, Integer> map = new HashMap<>();
        //获取键值流
        Set<String> keySet = map.keySet();
        Stream<String> keyStream = keySet.stream();
        //获取键值对流
        Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
        Stream<Map.Entry<String, Integer>> entryStream = entrySet.stream();
        //获取values流
        Collection<Integer> values = map.values();
        Stream<Integer> valueStream = values.stream();
        //3.数组获取流(Stream<T>接口的静态方法 of())
        Integer[] arr = new Integer[]{1, 2, 3, 4, 5};
        Stream<Integer> arrStream = Stream.of(arr);
        //of方法重载,数组可用变长数组代替
        Stream<Integer> arrStream1 = Stream.of(1, 2, 3, 4, 5);
    }
}

常用API:
void forEach(Consumer) - 终结方法
Stream filter(Predicate) - 延迟方法
Predicate中test返回true, 是保留在流中的
Stream map(Function<T, R>) - 延迟方法
将 流中的 T类型的数据, 转成 R类型数据, 并且存入新的流中
static Stream concat(Stream, Stream)
将两个流拼接成一个

代码实现:

import org.junit.Test;

import java.util.ArrayList;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class StreamAPI {
    //创建列表
    private static ArrayList<String> list;
    //给列表添加元素
    public void setList(){
        list = new ArrayList<>();
        list.add("黎明杀机");
        list.add("怪物猎人世界");
        list.add("饥荒");
        list.add("杀戮尖塔");
        list.add("星露谷物语");
        list.add("赛博朋克2077");
        list.add("神界:原罪2");
        list.add("文明6");
    }
    @Test//单元测试时并没有加载类,所以也没有运行静态方法
    public void forEachDemo(){
        setList();
        //forEach(消费者函数接口对象), 终结方法, 遍历消费
        list.stream().forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        System.out.println(list);
    }
    @Test
    public void filterDemo(){
        setList();
        //filter(判断接口对象), 延迟方法, 通过实现判断接口过滤流
        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.length() == 2;
            }
        }).forEach(s -> System.out.println(s));
        //流并不是对原集合进行操作,只是在改变基于原集合的函数模型
        System.out.println(list);
    }

    @Test
    public void mapDemo(){
        setList();
        //map(函数接口对象), 延迟方法, 通过实现函数接口将流中的元素映射到另一个流
        Stream<Integer> length = list.stream().map(new Function<String, Integer>(){
            @Override
            public Integer apply(String s) {
                return s.length();
            }
        });
        length.forEach(s -> System.out.println(s));
    }

    @Test
    public void countDemo(){
        setList();
        //count(), 终结方法, 返回元素个数,返回值为long类型
        long size = list.stream().count();
        System.out.println(size);
    }

    @Test
    public void limitDemo(){
        setList();
        //limit ⽅方法可以对流进行截取,只截取前n个。
        //limit(long size), 延迟方法, 参数是long类型,如果集合当前长度大于参数则进行截取;否则不进行操作。
        list.stream().limit(3).forEach(s -> System.out.println(s));
    }

    @Test
    public void skipDemo(){
        setList();
        //skip方法可以跳过前几个元素。
        //skip(long size), 延迟方法, 如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流
        list.stream().skip(3).forEach(s -> System.out.println(s));
    }

    @Test
    public void concatDemo(){
        setList();
        //concat方法可以组合两个流。
        //Stream.concat(Stream<T>, Stream<N>), 延迟方法, 将两个流合并为一个流
        Stream.concat(list.stream(), list.stream()).forEach(s -> System.out.println(s));
    }
}

案例

import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;

public class Example {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("黎明杀机");
        set.add("怪物猎人世界");
        set.add("饥荒");
        set.add("杀戮尖塔");
        set.add("星露谷物语");
        set.add("赛博朋克2077");
        set.add("神界:原罪2");
        set.add("文明6");
//        System.out.println("文明6".matches(".*\\d.*"));
//        System.out.println("饥荒".matches("^[\\u4E00-\\u9FA5]+$"));
        // 1. 第一个队伍只要名字全是中文游戏;存储到一个新集合中。
        // 2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
        Stream<String> queue1 = set.stream().filter(name -> name.matches("^[\\u4E00-\\u9FA5]+$")).limit(3);
        // 3. 第二个队伍只要名字含有数字的游戏;存储到一个新集合中。
        // 4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
        Stream<String> queue2 = set.stream().filter(name -> name.matches(".*\\d.*")).skip(2);
        // 5. 将两个队伍合并为一个队伍;存储到一个新集合中。
        Stream<String> appendQueue = Stream.concat(queue1, queue2);
        // 6. 根据名字创建Game对象;映射到一个新流中。
        Stream<Game> game = appendQueue.map(name -> new Game(name));
        // 7. 打印整个队伍的Game对象信息。
        game.forEach(g -> System.out.println(g.getName()));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值