Java 属性集、函数式接口和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));
}
}