✨✨个人主页:沫洺的主页
📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏
📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专栏
📖Docker专栏📖Reids专栏📖MQ专栏📖SpringCloud专栏
💖💖如果文章对你有所帮助请留下三连✨✨
🌾集合
前面学习了单例集合和双列集合,及它们的一些简单操作,实际开发中,我们往往要对集合中的数据进行处理,比如筛选一些具备需求的数据,如下案例
按照下面的要求完成集合的创建和遍历
创建一个集合,存储多个字符串元素
把集合中所有以"张"开头的元素存储到一个新的集合
把"张"开头的集合中的长度为3的元素存储到一个新的集合
遍历上一步得到的集合
package cn.moming16; import java.util.ArrayList; import java.util.Arrays; public class nameDemo { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(Arrays.asList("张三丰","张无忌","张翠山","张良","王二麻子","谢广坤")); //把集合中所有以"张"开头的元素存储到一个新的集合 ArrayList<String> newList = new ArrayList<>(); for (String name : list) { if(name.startsWith("张")){ newList.add(name); } } System.out.println(newList); //把"张"开头的集合中的长度为3的元素存储到一个新的集合 ArrayList<String> newList1 = new ArrayList<>(); for (String name1 : newList) { if(name1.length()==3){ newList1.add(name1); } } System.out.println(newList1); } }
[张三丰, 张无忌, 张翠山, 张良] [张三丰, 张无忌, 张翠山]
可以看到这种筛选方式是比较麻烦的
接下来使用流来操作
package cn.moming16; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class nameDemo { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(Arrays.asList("张三丰","张无忌","张翠山","张良","王二麻子","谢广坤")); List<String> newList = list.stream().filter(name -> name.startsWith("张")).collect(Collectors.toList()); System.out.println(newList); List<String> newList1 = newList.stream().filter(name -> name.length() == 3).collect(Collectors.toList()); System.out.println(newList1); } }
[张三丰, 张无忌, 张翠山, 张良] [张三丰, 张无忌, 张翠山]
上面代码其实就是通过集合对象获取流对象进行数据的筛选和保存
🍃Stream流
Stream流的三类方法
获取Stream流
- 创建一条流水线,并把数据放到流水线上准备进行操作
中间方法
- 流水线上的操作。
- 一次操作完毕之后,还可以继续进行其他操作。
终结方法
- 一个Stream流只能有一个终结方法
- 是流水线上的最后一个操作
Stream流的获取方法
单列集合(Collection体系集合 )
- 可以使用Collection接口中的默认方法stream()生成流
- default Stream<E> stream()
双列集合(Map体系 集合)
- 间接的生成流
- 可以先通过keySet或者entrySet获取一个Set集合,再获取Stream流
数组
- Arrays中的静态方法stream 生成流
同种数据类型的多个数据
- 1,2,3,4,5….
- “aaa”,“bbb”,“ccc”….
- 通过Stream接口的静态方法of(T... values)生成流
注意
- 通过stream流修改数据是不影响原集合的
- 一个stream流对象只能调用一次方法
package cn.moming16; import java.util.*; import java.util.stream.Stream; public class nameDemo { public static void main(String[] args) { //单列集合对象调用默认方法stream()生成流 //ArrayList ArrayList<Integer> arrayList = new ArrayList<>(); Stream<Integer> stream = arrayList.stream(); //LinkedList LinkedList<Double> linkedList = new LinkedList<>(); Stream<Double> stream1 = linkedList.stream(); //HashSet HashSet<String> hashSet = new HashSet<>(); Stream<String> stream2 = hashSet.stream(); //TreeSet TreeSet<Object> treeSet = new TreeSet<>(); Stream<Object> stream3 = treeSet.stream(); //双列集合把Map转成Set集合,间接的生成流 //HashMap //键单列 HashMap<String,Integer> hashMap = new HashMap<>(); Set<String> keySet = hashMap.keySet(); Stream<String> stream4 = keySet.stream(); //键值对单列 Set<Map.Entry<String, Integer>> entrySet = hashMap.entrySet(); Stream<Map.Entry<String, Integer>> stream5 = entrySet.stream(); //TreeMap TreeMap<Object, Object> treeMap = new TreeMap<>(); //键单列 Stream<Object> stream6 = treeMap.keySet().stream(); //键值对单列 Stream<Map.Entry<Object, Object>> stream7 = treeMap.entrySet().stream(); //数组 String[] str = {"a","b","c","d","e","f"}; Stream<String> stream8 = Arrays.stream(str); //通知数据类型的多个数据 Stream<Integer> stream9 = Stream.of(1, 2, 3, 4, 5); Stream<String> stream10 = Stream.of("aaa", "bbb", "ccc"); } }
Stream流的常见中间操作方法
中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作
方法名 说明 Stream<T> filter(Predicate predicate) 用于对流中的数据进行过滤 Stream<T> limit(long maxSize) 返回此流中的元素组成的流,截取前指定参数个数的数据 Stream<T> skip(long n) 跳过指定参数个数的数据,返回由该流的剩余元素组成的流 static <T> Stream<T> concat(Stream a, Stream b) 合并a和b两个流为一个流 Stream<T> distinct() 返回由该流的不同元素(根据Object.equals(Object) )组成的流 package cn.moming16; import java.util.*; import java.util.function.Predicate; import java.util.stream.Stream; public class nameDemo { public static void main(String[] args) { List<String> strings = Arrays.asList("张三丰", "张无忌", "王二麻子","张翠山", "张良", "王二麻子", "谢广坤"); ArrayList<String> list = new ArrayList<>(strings); //过滤出叫张三丰的 list.stream().filter(new Predicate<String>() { @Override public boolean test(String s) { boolean result = s.equals("张三丰"); return result; } }).forEach(s -> System.out.println(s)); System.out.println("======================"); //截取前2个 list.stream().filter(new Predicate<String>() { @Override public boolean test(String s) { boolean result = s.startsWith("张"); return result; } }).limit(2).forEach(System.out::println); System.out.println("======================"); //跳过前两个获取后面的 list.stream().skip(2).forEach(System.out::println); System.out.println("======================"); //合并a和b两个流为一个流 Stream<String> a = list.stream().limit(2); Stream<String> b = list.stream().skip(2); Stream<String> concat = Stream.concat(a, b); concat.forEach(System.out::println); System.out.println("======================"); //去除重复 list.stream().distinct().forEach(System.out::println); } }
张三丰 ====================== 张三丰 张无忌 ====================== 王二麻子 张翠山 张良 王二麻子 谢广坤 ====================== 张三丰 张无忌 王二麻子 张翠山 张良 王二麻子 谢广坤 ====================== 张三丰 张无忌 王二麻子 张翠山 张良 谢广坤
Stream流的常见终结操作方法
终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作
方法名 说明 void forEach(Consumer action) 对此流的每个元素执行操作 long count() 返回此流中的元素数 package cn.moming16; import java.util.*; public class nameDemo { public static void main(String[] args) { List<String> strings = Arrays.asList("张三丰", "张无忌", "王二麻子","张翠山", "张良", "王二麻子", "谢广坤"); ArrayList<String> list = new ArrayList<>(strings); //过滤出叫张三丰的 list.stream().filter(s -> s.equals("张三丰")).forEach(s -> System.out.println(s)); //返回此流中的元素数 long l = list.stream().filter(s -> s.equals("张三丰")).count(); System.out.println(l); } }
张三丰 1
Stream流的收集方法
方法名 说明 R collect(Collector collector) 把结果收集到集合中 工具类Collectors提供了具体的收集方式
方法名 说明 public static <T> Collector toList() 把元素收集到List集合中 public static <T> Collector toSet() 把元素收集到Set集合中 public static Collector toMap(Function keyMapper,Function valueMapper) 把元素收集到Map集合中 package cn.moming16; import com.sun.source.doctree.ValueTree; import java.security.Key; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; public class nameDemo { public static void main(String[] args) { List<String> strings = Arrays.asList("张三丰", "张无忌", "王二麻子","张翠山", "张良", "王二麻子", "谢广坤"); ArrayList<String> list = new ArrayList<>(strings); //把结果收集到集合中 ArrayList<String> list1 = new ArrayList<>(); List<String> collect1 = list.stream().collect(Collectors.toList()); Set<String> collect2 = list.stream().collect(Collectors.toSet()); collect1.forEach(System.out::println); System.out.println("================="); //把元素收集到Set集合中 collect2.forEach(System.out::println); System.out.println("================="); //把元素收集到Map集合中 HashMap<String, Integer> map = new HashMap<>(); map.put("张三丰",18); map.put("王二麻子",11); map.put("张良",10); map.put("王二麻子",20); map.put("谢广坤",30); Map<String, Integer> map1 = map.entrySet().stream().collect(Collectors.toMap(s -> s.getKey(), s -> s.getValue())); map1.forEach((key,value)-> System.out.println(key+"=="+value)); } }
张三丰 张无忌 王二麻子 张翠山 张良 王二麻子 谢广坤 ================= 张良 张翠山 王二麻子 张三丰 张无忌 谢广坤 ================= 张良==10 王二麻子==20 张三丰==18 谢广坤==30
🌲其他IO
🍂转换流
什么是转换流
- 转换流就是来进行字节流和字符流之间转换的
字符流中和编码解码问题相关的两个类
InputStreamReader:是从字节流到字符流的桥梁,父类是Reader
它读取字节,并使用指定的编码将其解码为字符
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
OutputStreamWriter:是从字符流到字节流的桥梁,父类是Writer
是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
转换流读写数据
构造方法
方法名 说明 InputStreamReader(InputStream in) 使用默认字符编码创建InputStreamReader对象 InputStreamReader(InputStream in,String chatset) 使用指定的字符编码创建InputStreamReader对象 OutputStreamWriter(OutputStream out) 使用默认字符编码创建OutputStreamWriter对象 OutputStreamWriter(OutputStream out,String charset) 使用指定的字符编码创建OutputStreamWriter对象 package cn.moming16; import java.io.*; public class ConversionStreamDemo { public static void main(String[] args) throws IOException { //字节流到字符流 //OutputStreamWriter osw1 = new OutputStreamWriter( // new FileOutputStream("mondule02\\src\\files\\a.txt")); OutputStreamWriter osw2 = new OutputStreamWriter( new FileOutputStream("mondule02\\src\\files\\a.txt"),"UTF-8"); osw2.write("沫洺"); osw2.close(); //字符流到字节流 //InputStreamReader isr1 = new InputStreamReader( // new FileInputStream("mondule02\\src\\files\\a.txt")); InputStreamReader isr2 = new InputStreamReader( new FileInputStream("mondule02\\src\\files\\a.txt"),"UTF-8"); int ch; while ((ch=isr2.read())!=-1){ System.out.println((char) ch); } isr2.close(); } }
🍁对象操作流
对象序列化介绍
对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中。
这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
对象操作流分为两类:对象操作输入流和对象操作输出流
- 对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
- 对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
对象序列化流: ObjectOutputStream
- 将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象
构造方法
方法名 说明 ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的ObjectOutputStream 序列化对象的方法
方法名 说明 void writeObject(Object obj) 将指定的对象写入ObjectOutputStream 注意事项
一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口
Serializable是一个标记接口,实现该接口,不需要重写任何方法
package cn.moming16; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class ObjectOutputStreamDemo { public static void main(String[] args) throws IOException { //序列化 //创建一个写入指定的OutputStream的ObjectOutputStream ObjectOutputStream oos=new ObjectOutputStream( new FileOutputStream("mondule02\\src\\files\\a.txt")); //创建对象 Student student = new Student("张三",18); //void writeObject(Object obj):将指定的对象写入ObjectOutputStream oos.writeObject(student); //释放资源 oos.close(); } }
对象反序列化流: ObjectInputStream
- ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
构造方法
方法名 说明 ObjectInputStream(InputStream in) 创建从指定的InputStream读取的ObjectInputStream 反序列化对象的方法
方法名 说明 Object readObject() 从ObjectInputStream读取一个对象 package cn.moming16; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; public class ObjectInputStreamDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { //反序列化 //创建从指定的InputStream读取的ObjectInputStream ObjectInputStream ois = new ObjectInputStream( new FileInputStream("mondule02\\src\\files\\a.txt")); //Object readObject():从ObjectInputStream读取一个对象 Object obj = ois.readObject(); //将读取出来的对象类型转换 Student student = (Student) obj; System.out.println(student.getName()+"=="+student.getAge()); //释放资源 ois.close(); } }
张三==18
用对象序列化流序列化了一个对象后,假如我们修改了对象所属的Javabean类,读取数据会不会出问题呢?
- 会出问题,会抛出InvalidClassException异常(无效的类异常)
当序列化运行时检测到类中的以下问题之一时抛出。
类的串行版本与从流中读取的类描述符的类型不匹配
该类包含未知的数据类型
该类没有可访问的无参数构造函数
解释
序列化运行时将每个可序列化的类与称为serialVersionUID的版本号相关联,该序列号在反序列化期间用于验证序列化对象的发送者和接收者是否已加载与该序列化兼容的对象的类。 如果接收方加载了一个具有不同于相应发件人类的serialVersionUID的对象的类,则反序列化将导致InvalidGlassException. 一个可序列化的类可以通过声明一个名为"serialVersionUID
"
的字段来显式地声明它自己的serialVersionUID,该字段必须是static,final,类型是long,如果可序列化类没有显式声明serialVersionUID,则序列化运行时将根据Java(TM)对象序列化规范中所述的类的各个方面计算该类的默认serialVersionUID值。直白的理解就是,当你修改了对象所属类的属性时,会发生版本不一致的冲突,此冲突会导致InvalidClassException异常
比如将上面的学生类中的toString方法删去
解决方法
- 可以重新序列化
- 或者给对象所属的类加一个serialVersionUID(private static final long serialVersionUID = 42L),再序列化,然后再对对象类进行修改,无论如何修改,都能反序列化。
- 修改后反序列化的前提是,已经加了serialVersionUID,且序列化过了
如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
- 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
🍁Properties集合
Properties介绍
是一个Map体系的集合类
Properties可以保存到流中或从流中加载
属性列表中的每个键及其对应的值都是一个字符串
Properties集合的基本使用
package cn.moming16; import java.util.Properties; import java.util.Set; public class PropertiesDemo { public static void main(String[] args) { Properties prop = new Properties();//本质是个Map集合 prop.put("张三丰", 18); prop.put("张三", 14); prop.put("李四", 15); prop.put("王五", 20); System.out.println(prop); //遍历 Set<Object> set = prop.keySet(); for (Object key : set) { Object value = prop.get(key); System.out.println(key+"=="+value); } } }
Properties作为Map集合的特有方法
方法名 说明 Object setProperty(String key, String value) 设置集合的键和值,都是String类型,底层调用 Hashtable方法 put String getProperty(String key) 使用此属性列表中指定的键搜索属性 Set<String> stringPropertyNames() 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 package cn.moming16; import java.util.Properties; import java.util.Set; public class PropertiesDemo { public static void main(String[] args) { Properties prop = new Properties();//本质是个Map集合 //设置集合的键和值,都是String类型,底层调用 Hashtable方法 put prop.setProperty("字符串类型1", "字符串类型2"); prop.setProperty("张三", "18"); prop.setProperty("李四", "19"); //使用此属性列表中指定的键搜索属性 String value = prop.getProperty("张三"); System.out.println(value); //从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 Set<String> set = prop.stringPropertyNames(); for (String key : set) { Object value1 = prop.get(key); System.out.println(key+"=="+value1); } System.out.println(set); } }
18 李四==19 张三==18 字符串类型1==字符串类型2 [李四, 张三, 字符串类型1]
Properties和IO流相结合的方法
方法名 说明 void load(InputStream inStream)
从输入字节流读取属性列表(键和元素对)
void load(Reader reader)
从输入字符流读取属性列表(键和元素对)
void store(OutputStream out, String comments)
将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流
void store(Writer writer, String comments)
将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流
将属性列表写入prop.properties
package cn.moming16; import java.io.FileWriter; import java.io.IOException; import java.util.Properties; public class PropertiesIODemo { public static void main(String[] args) throws IOException { Properties prop = new Properties(); prop.setProperty("啊飞","2022081601"); prop.setProperty("啊浩","2022081602"); prop.setProperty("啊涛","2022081603"); //将此属性列表(键和元素对)写入此Properties表中 //以适合使用load(Reader)方法的格式写入输出字符流 FileWriter fw = new FileWriter("mondule02\\src\\files\\prop.properties"); prop.store(fw,null); fw.close(); } }
如果出现乱码可以设置一下编码格式
将prop.properties文件里的属性列表加载到properties集合中
package cn.moming16; import java.io.FileReader; import java.io.IOException; import java.util.Properties; public class PropertiesIODemo1 { public static void main(String[] args) throws IOException { Properties prop = new Properties(); FileReader fr = new FileReader("mondule02\\src\\files\\prop.properties"); //void load(Reader reader):从输入字符流读取属性列表(键和元素对) prop.load(fr); fr.close(); System.out.println(prop); } }
{啊浩=2022081602, 啊涛=2022081603, 啊飞=2022081601}
💐综合案例
案例需求
在Properties文件中写上姓名和年龄,读取到集合中,将该数据封装成学生对象,序列化到本地文件
实现步骤
创建Properties集合,将本地文件中的数据加载到集合中
获取集合中的键值对数据,封装到学生对象中
创建序列化流对象,将学生对象序列化到本地文件中
先创建学生对象(可被序列化的且能修改)
package cn.mommg17; import java.io.Serializable; public class Student implements Serializable { private static final long serialVersionUID=1L; private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
向prop.properties文件中写学生的名字和年龄
package cn.mommg17; import java.io.*; import java.util.Properties; public class WriteDemo { public static void main(String[] args) throws IOException { //1.先创建几个学生对象,通过Properties集合和IO结合的方法将学生数据添加到prop.properties中 Student stu1 = new Student("张三",18); Student stu2 = new Student("李四",19); Student stu3 = new Student("王五",20); //2.创建Properties集合,向prop.properties文件中存入对象数据 Properties prop = new Properties(); FileWriter fw = new FileWriter("mondule02\\src\\files\\prop.properties"); prop.setProperty(stu1.getName(),(stu1.getAge()+""));//都是字符串类型 prop.setProperty(stu2.getName(),(stu2.getAge()+"")); prop.setProperty(stu3.getName(),(stu3.getAge()+"")); prop.store(fw,null); fw.close(); System.out.println(prop); } }
序列化文件中存储数据的学生对象
package cn.mommg17; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.ObjectOutputStream; import java.util.Properties; import java.util.Set; public class SerializationReadDemo { public static void main(String[] args) throws IOException { //创建Properties集合 Properties prop = new Properties(); 3.将本地文件(prop.properties)中的数据加载到prop集合中 FileReader fr = new FileReader("mondule02\\src\\files\\prop.properties"); prop.load(fr); fr.close(); System.out.println(prop); //4.获取集合中的键值对数据,封装到学生对象中 //获取键单列集合 Set<String> keys = prop.stringPropertyNames(); //5.创建序列化流对象,将学生对象序列化到本地文件中 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("mondule02\\src\\files\\prop.txt")); for (String name : keys) {//遍历获取学生名 String strAge = prop.getProperty(name);//通过学生名获取学生年龄 int age = Integer.parseInt(strAge);//String转换为int类型 Student student = new Student(name,age);//创建学生对象 oos.writeObject(student);//序列化学生对象 } oos.close();//关闭序列化流 } }
整个过程就是,先将学生对象的数据以键值对的方式存入到集合中,再根据集合中的键值对数据创建学生对象,再对学生对象序列化(方法很多)