Java 属性集、函数式接口和Stream流

Java属性集与函数式编程
本文深入探讨了Java中的属性集(Properties)类、函数式接口(如Supplier、Consumer、Predicate、Function)以及Stream流的基本概念和使用方法。通过实例讲解如何利用这些特性简化编程流程,提高代码效率。

1. 属性集

1.1 概述

  Properties继承于Hashtable,来表示一个持久的属性集。它使用键值结构存储数据,每个键与其对应值都是一个字符串。

1.2 Properties类

构造方法:

  • public Properties() :创建一个空的属性列表。

基本存储方法:

  • public Object setProperty(String key, String value):保存一对属性。
  • public String getProperty(String key) :使用此属性列表中指定的键搜索属性值。
  • public Set stringPropertyNames() :所有键的名称的集合。
  • public void load(InputStream inStream) :从字节输入流中读取键值对。

演示代码:

import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Set;

public class Demo01 {
    @Test
    public void m1() throws IOException {
        //1.创建属性集对象
        Properties pro = new Properties();

        //2.通过流加载属性集
//        String Path = Demo01.class.getClassLoader().getResource("config.properties").getPath();
//        pro.load(new FileInputStream(Path));
        InputStream is = Demo01.class.getClassLoader().getResourceAsStream("config.properties");
        pro.load(is);

        //3.获取属性集中的value值
        String name = pro.getProperty("name");
        System.out.println(name);

        //4.直接给属性集设置属性
        pro.setProperty("url", "http://www.baidu.com");
        String url = pro.getProperty("url");
        System.out.println(url);

        //5.通过Unicode编码编译中文
        String userName = pro.getProperty("userName");
        System.out.println(userName);

        //6.将“男”的编码变回ISO8859-1 然后再用UTF-8进行转换
        String sex = pro.getProperty("userSex");
        String str = new String(sex.getBytes("ISO8859-1"), "UTF-8");
        //UTF-8可省略
        //String str = new String(sex.getBytes("ISO8859-1")");
        System.out.println(str);
        System.out.println();

        Set<String> strings = pro.stringPropertyNames();
        for (String key : strings) {
            System.out.println(key + " -- " + pro.getProperty(key));
            //System.out.println(key + " -- " + new String((pro.getProperty(key)).getBytes("ISO8859-1"), "UTF-8"));
        }
    }
}

运行结果:
在这里插入图片描述

2. 常用函数式接口

常用函数式接口包括以下几种:

  • Supplier
  • Consumer
  • Predicate
  • Function

2.1 Supplier接口

  Supplier接口仅包含一个无参的方法:

  • T get() 用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,也就意味着对应的Lambda表达式“对外提供”一个符合泛型类型的对象数据。
import java.util.function.Supplier;

public class Demo01 {
    /*
    为了使用Lambda表达式, 所以将Supplier作为方法参数使用
    方法的目的: 为了返回一个字符串对象
     */
    public static String getInstance(Supplier<String> sup) {
        // 调用Supplier 的get方法
        return sup.get();
    }
    public static void main(String[] args) {
        // 调用getInstance方法, 需要传递一个Supplier接口实现类
        // 又因为Supplier接口是函数式接口, 所以可以使用Lambda表达式
        String str = getInstance(() -> {
            // 这是生产字符串的过程
            return "hello";});
        System.out.println(str);
    }
}

使用Supplier接口作为方法的参数类型,通过Lambda表达式求出int数组中的最大值。

import java.util.function.Supplier;

public class Demo02 {
    public static int getMax(int[] arr, Supplier<Integer> sup) {
        return sup.get();
    }

    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 3, 3, 2, 1, 2, 3};
        int m = getMax(arr, () -> {
            int max = 0;
            for (int i : arr) {
                if (max < i) {
                    max = i;
                }
            }
            return max;
        });
        System.out.println(m);

        int[] arr1 = {6, 4, 2, 2, 1, 4, 6, 3, 2};
        int n = getMax(arr1, () -> {
            int min = 2;
            for (int i : arr1) {
                if (min > i) {
                    min = i;
                }
            }
            return min;
        });
        System.out.println(n);
    }
}

2.2 Consumer接口

Consumer接口则与Supplier接口相反,它不是产生一个数据,而是消费一个数据,其数据类型由泛型决定。

  • 抽象方法:accept:意为消费一个指定泛型的数据。
  • 默认方法:andThen:将两个消费方式组合在一起。
import java.util.function.Consumer;

public class Demo02 {
    public static void m1(String str, Consumer<String> cons1, Consumer<String> cons2) {
        cons1.andThen(cons2).accept(str);
    }

    public static void main(String[] args) {

        m1("JAVAgood", s -> {
            System.out.println(s.toUpperCase());
        }, s -> {
            System.out.println(s.toLowerCase());
        });
    }
}

格式化打印练习:

import java.util.function.Consumer;

public class Demo03 {

    public static void m1(String[] str, Consumer<String> cons1, Consumer<String> cons2) {
        for (String info : str) {
            cons1.andThen(cons2).accept(info);
        }
    }

    public static void main(String[] args) {
        String[] arr = {"?,男", "??,男", "???,女"};
        m1(arr,
                s -> System.out.print("姓名:" + s.split(",")[0]),
                s -> System.out.println("性别:" + s.split(",")[1]));
    }
}

2.3 Predicate接口

Predicate接口可以对某种类型的数据进行判断,从而得到一个boolean值结果。

  • 抽象方法:test 用于条件判断。
import java.util.function.Predicate;

public class Demo01Test {
    public static void m1(Predicate<String> p1) {
        boolean b = p1.test("hello world!");
        System.out.println("读取字符串的长度大于5吗?" + b);
    }

    public static void main(String[] args) {
        m1(s -> s.length() > 5);
    }
}
  • 默认方法:and 相当于逻辑关系中的“与”。
import java.util.function.Predicate;

public class Demo02And {
    public static void m1(String str, Predicate<String> p1, Predicate<String> p2) {
        boolean isValid = p1.and(p2).test(str);
        System.out.println("字符串符合要求吗?" + isValid);
    }

    public static void main(String[] args) {
        m1("hello world",
                s -> s.contains("h"),
                s -> s.contains("o"));
    }
}
  • 默认方法:or 相当于逻辑关系中的“或”。
import java.util.function.Predicate;

public class Demo03Or {
    public static void m1(String str, Predicate<String> p1, Predicate<String> p2) {
        boolean isValid = p1.or(p2).test(str);
        System.out.println("字符串符合要求吗?" + isValid);
    }

    public static void main(String[] args) {
        m1("hello world",
                s -> s.contains("h"),
                s -> s.contains("1"));
    }
}
  • 默认方法:negate 相当于逻辑关系中的“非(取反)”。
import java.util.function.Predicate;

public class Demo04Negate {
    public static void m1(String str, Predicate<String> p1) {
        boolean isValid = p1.negate().test(str);
        System.out.println("字符串符合要求吗?取反!!! " + isValid);
    }

    public static void main(String[] args) {
        m1("hello world",
                s -> s.contains("h"));
    }
}

综合运用:

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

public class Demo05 {

    public static void main(String[] args) {

        String[] arr = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女"};
        List<String> l1 = m1(arr, s -> s.split(",")[0].length() == 4, s -> s.split(",")[1].equals("女"));
        System.out.println(l1);
    }

    public static List<String> m1(String[] str, Predicate<String> p1, Predicate<String> p2) {
        List<String> arrayList = new ArrayList();
        for (String info : str) {
            if (p1.and(p2).test(info)) {
                arrayList.add(info);
            }
        }
        return arrayList;
    }
}

2.4 Function接口

Function接口用来根据一个类型的数据得到另一个类型的数据,前置称为前置条件,后者称为后置条件。

  • 抽象方法:R apply(T t);根据类型T参数获取类型R的结果。例如将String类型转换为Integer类型。
  • 默认方法:andThen;将两个方法组合在一起。

演示代码

import java.util.function.Function;

public class Demo03 {
    public static void m1(String str, Function<String, String> f1,
                          Function<String, Integer> f2, Function<Integer, Integer> f3) {
        int i = f1.andThen(f2).andThen(f3).apply(str);
        System.out.println(i);
    }

    public static void main(String[] args) {
        m1("赵丽颖, 20", s -> s.split("[. ]+")[1],
                s -> Integer.valueOf(s),
                s -> s + 100);
    }
}

3. Stream流

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

  • 元素是特定类型的对象,形成一个队列。Java中的Stream并不会存储元素,而是按需计算。
  • 数据源流的来源可以是集合,数组等。

Stram的两个基本特征:

  • pipelining:中间操作本身都会返回流对象本身。这样多个操作可以串联成一个管道,如同流式风格。
  • 内部迭代:以前对集合的遍历都是通过iterator或者foreach的方式,显示在集合外部进行迭代,称为外部迭代。Stream提供了内部迭代的方式,流可以直接调用遍历的方法。

3.1 获取流

Stream是java 8 新加入的最常用的流接口。

  • 所有的Collection集合都可以通过Stream默认方法获取流。
  • Stram接口的静态方法of可以获取数组对应的流。

Collection获取流:

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

public class Demo03 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        Set<String> set = new HashSet<>();
        Stream<String> stream2 = set.stream();

        Vector<String> vector = new Vector<>();
        Stream<String> stream3 = vector.stream();
    }
}

Map获取流:

Map接口不是Collection的子接口,其K-V数据结构不符合流元素的单一特征,所以获取对应的流需要分key、value和entry等情况。

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

public class Demo03Map {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        Stream<String> keyStream = map.keySet().stream();
        Stream<String> valueStream = map.values().stream();
        Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
    }
}

数组获取流:

import java.util.stream.Stream;

public class Demo04ARR {
    public static void main(String[] args) {
        String[] array = {"张三", "李四", "王五", "六"};
        Stream<String> stream = Stream.of(array);
    }
}

3.2 常用方法

  • 延迟方法:返回值类型仍然是Stream接口自身类型的方法,因此支持链式调用。(处理终结方法外,其余方法均为延迟方法)
  • 终结方法:返回值类型不再是Stream接口自身类型的方法,因此不再支持链式调用。
		- 终结方法
	   void forEach(Consumer) ;将每个流元素交给函数进行处理。
		
	   long count();  为流计算元素个数
       		
        - 延迟方法
       Stream<T> filter(Predicate)  将一个流转换为另一个子集流
            
       Stream<R> map(Function<T, R>)  将流中的T类型的数据, 转成R类型数据, 并且存入新的流中
            
       Stream<T> skip(long n);跳过几个元素
       
       Stream<T> limit(long maxSize);对流进行截取
       
       static Stream<T> concat(Stream, Stream) 将两个流组合

3.3 Stream流的运用

import org.w3c.dom.ls.LSOutput;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Stream;

public class Demo01 {
    public static void main(String[] args) {
        ArrayList<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("石破天");
        one.add("石中玉");
        one.add("老子");
        one.add("庄子");
        one.add("洪七公");

        ArrayList<String> twe = new ArrayList<>();
        twe.add("古力娜扎");
        twe.add("张无忌");
        twe.add("赵丽颖");
        twe.add("张三丰");
        twe.add("尼古拉斯赵四");
        twe.add("张天爱");
        twe.add("张二狗");

        //1.第一只队伍只要名字为3的成员姓名:存储到一个新集合中。
        one.stream().filter(name -> name.length() == 3)
                .forEach(name -> System.out.println(name));
        System.out.println();
        //2.第一只队伍筛选之后只要前3个人;储存到一个新的集合中。
        one.stream().filter(name -> name.length() == 3).limit(3)
                .forEach(name -> System.out.println(name));
        Stream<String> stream1 = one.stream().filter(name -> name.length() == 3).limit(3);
        System.out.println();
        //3.第二个队伍只要姓张的成员姓名,储存到新的集合中
        twe.stream().filter(name -> name.startsWith("张"))
                .forEach(name -> System.out.println(name));
        System.out.println();
        //4.第二个队伍筛选后不要前2个人
        twe.stream().filter(name -> name.startsWith("张")).skip(2)
                .forEach(name -> System.out.println(name));
        Stream<String> stream2 = twe.stream().filter(name -> name.startsWith("张")).skip(2);
        System.out.println();
        //5.将2个队伍合并为一个队伍
        Stream<String> StreamC = Stream.concat(stream1, stream2);
        //6.根据姓名创建Person对象
        Stream<Person> p = StreamC.map(s -> new Person(s));
        //7.打印整个队伍的信息
        p.forEach(p1 -> System.out.println(p1));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值