多线程和Stream流

多线程和Stream流

1. 多线程

1.1 进程
	进程是一个独立的程序,每一个程序相对互斥,互不干扰。进程所需的资源(CPU, 内存,硬盘,网络,显卡)是由操作系统分配。
	Windows 操作系统是一个多任务,多线程操作系统吗???
	不是一个多任务,多线程的操作系统。
		时间片 ==> 操作系统 + CPU 执行一次任务的完整时间。
		例如: 1s ==> 100 份 10个程序
			A 15 B 5 C 12 D 8 

注意:
	1. 进程是一个独立的程序,需要操作系统分配资源
	2. 操作系统不是一个多任务,多线程的操作系统,是单位时间片多个任务切换给使用者的一个体验

1.2 线程
多线程
	电脑管家:可以同时执行病毒查杀,垃圾清理,电脑检查,权限管理,软件更新....
	每一个功能模块都是一个【线程】

	线程需要进程环境,因为线程使用的资源都是当前进程提供的!!!提供的资源有(CPU, 内存,硬盘,网络,显卡),并且线程之前有一定的抢占操作。也可以认为在抢占进程的执行时间片。
	一个进程至少有一个有效线程,如果当前进程没有任何一个线程存在,整个进程退出关闭

Java程序你认为最少有几个线程???分别是哪些线程???
	至少两个线程
		1. main线程【主线程】
		2. JVM 的 GC (Garbage Collector) 机制
1.3 进程和线程的关系
对比 进程 线程
资源分配 CPU,操作系统分配 当前进程分配资源
组成 是由多个线程组成 线程关注任务
独立性 进程是相对独立的 不可能独立存在,需要进程环境
1.4 多线程优势
物极必反!!!多线程的优势即劣势
优势:
	1. 提高资源利用率
	2. 提升用户体验
	3. 提高执行效率

劣势:
	1. 资源利用过高,导致计算机压力大。
	2. 导致用户体验降低
	3. 单个线程执行效率降低
	4. 极易导致死锁
1.5 多线程代码实现的两种方式
方式一:
 	继承 Thread 类,重写 run 方法,run 方法中是目标线程代码。【What will be run】
 	利用 Thread 类内的 start 开启线程。

方式二:
	遵从 Runnable 接口,实现 run 方法,run 方法中是目标线程代码、
	实例化 Thread 类对象,调用 Thread(Runnable) 构造方法,利用 start 开启线程

推荐方式二
	1. Java 中的类都是单继承类,如果继承 Thread 类之后无法继承其他类,影响代码的继承结构
	2. 遵从接口不影响继承过程,同时对当前代码是一个增强,耦合度低
package com.qfedu.a_thread;

/*
 * 两种方式实现 线程代码
 * 	1. 继承 Thread 类
 *  2. 遵从 Runnable 接口 
 */
/*
 * 方式一 继承 Thread 类
 */
class MyThread1 extends Thread {
	/*
	 * run 方法是线程执行目标的核心方法, what will be run。
	 * 当前线程对象启动,会执行 run 方法内容,如果是单独调用 run 方法
	 * 实际上非线程代码。
	 */
	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println("继承 【Thread】 类, 实现线程代码");
		}
	}
}

/*
 * 方式二 遵从 Runnable 接口
 */
class MyThread2 implements Runnable {
	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println("遵从 【Runnable】接口, 实现线程代码");
		}
	}
}

public class Demo1 {
	public static void main(String[] args) {
		// 方式一线程启动
		new MyThread1().start();
		
		// 方式二线程启动
		new Thread(new MyThread2()).start();
		
		for (int i = 0; i < 100; i++) {
			System.out.println("主线程【main 线程】代码!!!");
		}
	}
}
1.6 Thread 涉及到的方法
构造方法:
	Thread();
		Thread 无参数构造方法
	Thread(Runnable target);
		使用 Runnable 接口实现类作为 Thread 构造方法参数对象,明确当前 Thread 线程执行目标是什么。
	Thread(Runnable target, String name);
		使用 Runnable 接口实现类作为 Thread 构造方法参数对象,明确当前 Thread 线程执行目标是什么,同时使用
		name 设置当前线程的名称

成员方法:
	void setName(String name);
    	设置线程的名称
    String getName();
    	获取线程名
    void setPriority(int newPriority);
    	设置线程优先级,
    	tips
    		1. 线程优先级从 1 开始,最大为 10, 默认为 5。
			2. 线程优先级只可以提示线程的执行概率,不明确首先执行,第一个执行,本次时间片一定执行。
				Thread.MIN_PRIORITY = 1
				Thread.NORM_PRIORITY = 5
				Thread.MAX_PRIORITY = 10
				
    int getPriority();    
    	获取线程优先级
    void setDaemon(boolean daemon);
    	设置当前线程为守护线程,参数如果为 true 表示当前线程为守护线程。
    boolean isDaemon();
    	判断当前线程是否为守护线程
    	
    static Thread currentThread();
    	【静态方法】
    		在哪一个线程代码中执行,获取当前代码对应线程对象
    static void sleep(int ms);
    	【静态方法】
    		在哪一个线程代码中执行,当前线程进入阻塞状态,休眠指定的毫秒数
package com.qfedu.a_thread;

/*
构造方法:
	Thread();
		Thread 无参数构造方法
	Thread(Runnable target);
		使用 Runnable 接口实现类作为 Thread 构造方法参数对象,明确当前 Thread 线程执行目标是什么。
	Thread(Runnable target, String name);
		使用 Runnable 接口实现类作为 Thread 构造方法参数对象,明确当前 Thread 线程执行目标是什么,同时使用
		name 设置当前线程的名称

成员方法:
	void setName(String name);
    	设置线程的名称
    String getName();
    	获取线程名
    void setPriority(int newPriority);
    	设置线程优先级,
    	tips
    		1. 线程优先级从 1 开始,最大为 10, 默认为 5。
			2. 线程优先级只可以提示线程的执行概率,不明确首先执行,第一个执行,本次时间片一定执行。
				Thread.MIN_PRIORITY = 1
				Thread.NORM_PRIORITY = 5
				Thread.MAX_PRIORITY = 10
				
    int getPriority();    
    	获取线程优先级
    void setDaemon(boolean daemon);
    	设置当前线程为守护线程,参数如果为 true 表示当前线程为守护线程。
    boolean isDaemon();
    	判断当前线程是否为守护线程
    	
    static Thread currentThread();
    	【静态方法】
    		在哪一个线程代码中执行,获取当前代码对应线程对象
    static void sleep(int ms);
    	【静态方法】
    		在哪一个线程代码中执行,当前线程进入阻塞状态,休眠指定的毫秒数
 */
public class Demo2 {
	public static void main(String[] args) {
		Thread thread1 = new Thread();
		
		Thread thread2 = new Thread(() -> {
			System.out.println("lambda 表达式实现线程执行目标");
			/*
			 * 在哪一个线程代码中执行,获取当前代码对应线程对象
			 */
			Thread ct = Thread.currentThread();
			System.out.println(ct);
		});
		Thread thread3 = new Thread(() -> {
			System.out.println("lambda 表达式实现线程执行目标");
		}, "河霖想要周三交抄写String+反射十遍线程");
		
		/*
		 * Thread[Thread-0,5,main]
		 * Thread[threadName, threadPriority, threadGroup]
		 * Thread[线程名, 线程优先级, 线程组]
		 */
		System.out.println(thread1);
		System.out.println(thread2);
		System.out.println(thread3);
		System.out.println();
		
		// 设置和获取线程名
		thread2.setName("吉祥祥哥想要周二(2022年3月29日)交作业");
		System.out.println(thread2);
		System.out.println(thread1.getName());
		
		System.out.println();
				
		// 设置和获取线程优先级
		thread2.setPriority(Thread.MAX_PRIORITY);
		System.out.println(thread2.getPriority());
		
		/*
		 * 当前方法获取的哪一个线程对象
		 * 在哪一个线程代码中执行,获取当前代码对应线程对象
		 */
		Thread ct = Thread.currentThread();
		System.out.println(ct);
		
		thread2.start();
		
	}
}
1.7 同步机制
1.7.1 同步代码块
案例:
	电影院 <<钢铁侠>> 100 票
	猫眼,淘票票,美团

第一个问题:
	100 张票是不是共享资源???
		是
	
第二个问题:
	三个销售途径是否可以认为是三个线程???
		是

同步代码块
	在同步代码块中,有且只允许一个线程进入,线程进入【锁生效】。线程执行完毕【锁开启】。锁开启 之后允许其他线程进入抢占执行。
	
synchronized (锁对象) {
	同步代码块
}
锁对象要求:
	1. 必须是符合 Java 规格的一个类对象,不能是【包装类】
	2. 要求限制的线程,必须使用同一个锁对象。
	3. 常用锁对象
		线程之前的共享资源,类锁(考虑当前类有且只有一组相关线程)
package com.qfedu.a_thread;


/*
 * 100 张票作为三个线程对象的共享资源
 */
class SaleTicket implements Runnable {
	/*
	 * static 修饰的静态成员变量在对应当前类的所有对象是一个共享资源
	 * 同时使用 private 限制操作形式,针对于 ticket 操作全部由设定
	 * 的 方法来完成,初始化数据为 100
	 */
	private static int ticket = 100;
	
	/*
	 * @Override 是开启代码格式严格检查,确定子类重写父类方法,或者子类实现父类 or 接口
	 * 中方法,方法声明格式是否一致
	 */
	@Override
	public void run() {
		/*
		 *  获取当前线程对应的名称
		 */
		
		String threadName = Thread.currentThread().getName();
		while (true) {
			
			synchronized (SaleTicket.class) {
				if (ticket > 0) {
					
					System.out.println(threadName + "售出了 编号为 " + ticket + " 电影票" );
					ticket--;
				} else {
					System.out.println(threadName + "售罄");
					break;
				}
			}
		}
	}
}

public class Demo4 {
	public static void main(String[] args) {
		Thread t1 = new Thread(new SaleTicket(), "淘票票");
		Thread t2 = new Thread(new SaleTicket(), "美团");
		Thread t3 = new Thread(new SaleTicket(), "猫眼");
		
		t1.start();
		t2.start();
		t3.start();
	}
}

1.7.2 同步方法
package com.qfedu.a_thread;

class SaleTicket2 implements Runnable {
	/*
	 * static 修饰的静态成员变量在对应当前类的所有对象是一个共享资源
	 * 同时使用 private 限制操作形式,针对于 ticket 操作全部由设定
	 * 的 方法来完成,初始化数据为 100
	 */
	private static int ticket = 100;
	
	@Override
	public void run() {
		while (ticket > 0) {
			sale();
		}
	}
	
	/*
	 * synchronized 【静态】同步方法,要求当前方法体执行有且只允许一个线程进入。
	 * 可以认为作为所标记的对象 ==> SaleTicket2.class
	 * 
	 * 一个类内有多个 synchronized 修饰的同步方法,某一个方法执行,其他方法
	 * 都无法执行。可以保证执行目标的唯一性,保证数据安全,但是效率低!!!
	 */
	public static synchronized void sale() {
		String threadName = Thread.currentThread().getName();
		if (ticket > 0) {
			System.out.println(threadName + "售出了 编号为 " + ticket + " 电影票" );
			ticket--;
		} else {
			System.out.println(threadName + "售罄");
		}
	}
	
}

public class Demo5 {
	public static void main(String[] args) {
		Thread t1 = new Thread(new SaleTicket2(), "淘票票");
		Thread t2 = new Thread(new SaleTicket2(), "美团");
		Thread t3 = new Thread(new SaleTicket2(), "猫眼");
		
		t1.start();
		t2.start();
		t3.start();
	}	
}

2. Stream 流式操作

2.1 Stream 基本涉及到的方法
一般用于处理集合数据。可以基于 Stream 流完成流水线操作,提高开发效率,满足开发要求
Collection<E>
	Stream<E> stream(); 
		通过集合对象调用,可以获取对应当前集合的 Stream 流对象,同时在 Stream 中对应处理的数据类型为当前集合
		存储数据类型

中间方法:
	Stream<E> skip(int n);
    	跳过指定数据个数
	Stream<E> limit(int n);
		限制当前 Stream 流对应的数据总数
	Stream<E> filter(Predicate<E> pre);
		根据提供的 Predicate 接口过滤器,限制 Stream 流中数据存储内容
	Stream<E> distinct();
		去重操作,Stream 流中重复元素仅保留一个
	Stream<E> sorted(Comparator<E> com);
		Stream 流数据内容按照 Comparator 比较器规则排序
	Stream<R> map(Function<E, R> fun);
		Stream 数据内容根据 Function 接口限制规则进行数据类型转换,转换为对应 R 类型
		Stream<String> ==> Function ==>  Stream<Person>
	
最终方法:
	int count();
		当前 Stream 流对应有多少元素
	void forEach(Consumer<E> handler);
		当前 Stream 流对应元素的最终处理方式
2.2 基本演示
package com.qfedu.b_stream;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class Demo2 {
	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<String>();

		list.add("酱牛肉");
		list.add("羊肉烩面");
		list.add("羊肉泡馍");
		list.add("烤羊排");
		list.add("羊杂汤");
		list.add("牛肉汤");
		list.add("罐焖牛肉");
		list.add("辣子鸡丁");
		
		/*
		 * 1. 去除前两个元素
		 * 2. 仅保留 5 个元素
		 * 3. 要求元素带有 "肉"
		 */
		// 原始方式
		List<String> subList = list.subList(2, list.size());
		List<String> subList2 = subList.subList(0, 5);
		
		ArrayList<String> list3 = new ArrayList<String>();
		for (int i = 0; i < subList2.size(); i++) {
			if (subList2.get(i).contains("肉")) {
				list3.add(subList2.get(i));
			}
		}
		
		System.out.println(list3);
		
		// Stream 流 JDK1.8 新特征
		List<String> list2 = list
				.stream()
				.skip(2)
				.limit(5)
				.filter(s -> s.contains("肉"))
				.collect(Collectors.toList());
		System.out.println(list2);
		
	}
}
2.3 skip 跳过 和 limit 限制
package com.qfedu.b_stream;

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

public class Demo3 {
	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<String>();

		list.add("酱牛肉");
		list.add("羊肉烩面");
		list.add("羊肉泡馍");
		list.add("烤羊排");
		list.add("羊杂汤");
		list.add("牛肉汤");
		list.add("罐焖牛肉");
		list.add("辣子鸡丁");
		
		/*
		 * 集合对象获取 Stream 流对象
		 */
		Stream<String> stream = list.stream();
		
		/*
		 * Stream<E> skip(long n);
		 * 		跳过用户指定个数的数据内容 
		 */
		Stream<String> skip = stream.skip(3);
		
		/*
		 * Stream<E> limit(long maxSize);
		 * 		允许当前 Stream 流对象中最大容量是多少
		 */
		Stream<String> limit = skip.limit(3);
		
		limit.forEach(s -> System.out.println(s));
	}
}
2.4 filter 过滤
package com.qfedu.b_stream;

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

public class Demo4 {
	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<String>();

		list.add("酱牛肉");
		list.add("羊肉烩面");
		list.add("羊肉泡馍");
		list.add("烤羊排");
		list.add("羊杂汤");
		list.add("牛肉汤");
		list.add("罐焖牛肉");
		list.add("辣子鸡丁");
		
		Stream<String> stream = list.stream();
		
		/*
		 * Stream<T> filter(Predicate<T> predicate);
		 * 		过滤方法,过滤规则由 Predicate 过滤器接口决定,要求完成的方法
		 * 		boolean test(T t);
		 * 当前 Stream 流对应处理的数据类型为 字符串类型 String 类型。
		 * 		boolean test(String str);
		 * 可以利用 Lambda 表达式,完成一个处理参数为 String 类型返回值结果为
		 * boolean Lambda 表达式
		 */
		Stream<String> filter = stream.filter(s -> s.contains("肉"));
		
		filter.forEach(s -> System.out.println(s));
		
	}
}
@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
}
2.5 distinct 去重
package com.qfedu.b_stream;

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

public class Demo5 {
	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<String>();

		list.add("酱牛肉");
		list.add("羊肉烩面");
		list.add("羊肉泡馍");
		list.add("烤羊排");
		list.add("羊杂汤");
		list.add("牛肉汤");
		list.add("罐焖牛肉");
		list.add("辣子鸡丁");
		list.add("羊杂汤");
		list.add("牛肉汤");
		list.add("罐焖牛肉");
		list.add("辣子鸡丁");
		list.add("羊杂汤");
		list.add("牛肉汤");
		list.add("罐焖牛肉");
		list.add("辣子鸡丁");
		list.add("羊杂汤");
		list.add("牛肉汤");
		list.add("罐焖牛肉");
		list.add("辣子鸡丁");
		
		Stream<String> stream = list.stream();
		
		/*
		 * Stream<T> distinct();
		 * 		当前 Stream 流对应数据内容,重复元素仅保留一个
		 */
		Stream<String> distinct = stream.distinct();
		distinct.forEach(s -> System.out.println(s));
	}
}
2.6 sorted 排序
package com.qfedu.b_stream;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.function.Consumer;
import java.util.stream.Stream;

public class Demo6 {
	public static void main(String[] args) {
		ArrayList<Integer> list = new ArrayList<Integer>();
		
		list.add(5);
		list.add(2);
		list.add(7);
		list.add(6);
		list.add(3);
		list.add(1);
		list.add(4);
		list.add(10);
		list.add(8);
		list.add(9);
		
		Stream<Integer> stream = list.stream();
		
		/*
		 * Stream<T> sorted();
		 * 		当前Stream 流处理元素有自然顺序或者有比较方式(遵从Comparable接口)
		 * 		默认为升序方式
		 */
		// Stream<Integer> sorted = stream.sorted();
		/*
		 * Stream<T> sorted(Comparator<? super T> comparator);
		 * 		要求提供可以处理当时 Stream 流处理元素的 Comparator 比较器
		 * 		可以采用 Lambda 方式来完成
		 * 			 int compare(T o1, T o2);
		 *		目前 Stream 流处理的数据类型为 integer 类型
		 *			int compare(Integer o1, Integer o2);
		 *		Lambda 表达式 两个参数都是 Integer 类型,返回值要求为 int 类型
		 */
		Stream<Integer> sorted = stream.sorted((o1, o2) -> o2 - o1); 
		
		/*
		sorted.forEach(new Consumer<Integer>() {

			@Override
			public void accept(Integer t) {
				System.out.println(t);
			}
		});
		*/
		sorted.forEach(t -> System.out.println(t));
	}
}
2.7 map 转换方法
package com.qfedu.b_stream;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.stream.Stream;

import com.qfedu.util.BeanUtils;

public class Demo7 {
	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<String>();
		
		list.add("id=1,name=王某平,age=95,gender=男,score=0.5");
		list.add("id=2,name=王乾,age=99,gender=男,score=0.2");
		list.add("id=3,name=王豪豪,age=25,gender=男,score=99.5");
		list.add("id=4,name=王乐,age=16,gender=女,score=100");
		
		Stream<String> stream = list.stream();
		
		/*
		 * 执行 map 方法,同时提供类型转换器
		 * <R> Stream<R> map(Function<? super T, ? extends R> mapper);
		 * 		参数是一个 Function 类型转换器接口
		 * 			R apply(T t);
		 * 		Stream 流中目前处理的数据类型为 String 类型
		 * 			R apply(String t)
		 * 		R ==> Student 类型
		 * 			Student apply(String t);
		 * 		Lambda 表达式需要在返回值中明确返回类型为 Student 类型
		 */
		Stream<Student> map = stream.map(t -> {
			// 每一行字符串对应一个 Student 类对象
			Student stu = new Student();
			
			// t ==> id=1,name=王某平,age=95,gender=男,score=0.5 按照 , 切割
			String[] split = t.split(",");
			for (int i = 0; i < split.length; i++) {
				// 案例 id=1
				int index = split[i].indexOf("=");
				// fieldName = "id"
				String fieldName = split[i].substring(0, index);
				// value = "1"
				String value = split[i].substring(index + 1);
				
				// System.out.println(fieldName + "          " + value);
				try {
					BeanUtils.setProperty(stu, fieldName, value);
				} catch (NoSuchFieldException | SecurityException | NoSuchMethodException | IllegalAccessException
						| IllegalArgumentException | InvocationTargetException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			return stu;
		});
		
		map.forEach(System.out::println);
	}
}

package com.qfedu.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class BeanUtils {
	/**
	 * 给予符合 JavaBean 规范类对象赋值指定成员变量 fieldName,赋值数据为 String 字符串 value
	 * 
	 * @param bean      符合 JavaBean 规范类对象
	 * @param fieldName 指定赋值对应的成员变量名称
	 * @param value     对应当前成员变量的数据,类型为 String 类型
	 * @throws SecurityException         安全异常
	 * @throws NoSuchFieldException      没有对应成员变量异常
	 * @throws NoSuchMethodException     没有对应成员方法异常
	 * @throws InvocationTargetException 执行目标异常
	 * @throws IllegalArgumentException  非法参数异常
	 * @throws IllegalAccessException    非法权限异常
	 */
	public static void setProperty(Object bean, String fieldName, String value) throws NoSuchFieldException, SecurityException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		// 1. 确定当前 JavaBean 对象对应的是哪一个数据类型,获取对应的 Class 对象
		Class<? extends Object> cls = bean.getClass();
		
		// 2. 根据成员变量名称获取对应的成员变量类 Field 对象
		Field field = cls.getDeclaredField(fieldName);
		// 3. 给予对应的成员变量操作权限
		field.setAccessible(true);
		
		// 4. 获取成员变量对应数据类型
		Class<?> fieldType = field.getType();
		
		/*
		 * 1. String 直接用
		 * 2. char or Character 类型 charAt(0)
		 * 3. int or Integer 类型 Integer.parseInt(String)
		 * 4. Byte Short Long Float Double Boolean
		 * 		static Byte parseByte(String)
		 * 		static Short parseShort(String)
		 * 		static Long parseLong(String)
		 * 		static Float parseFloat(String)
		 * 		static Double parseDouble(String)
		 * 		static Boolean parseBoolean(String)
		 */
		
		// 5. 字符串数据类型转换
		Object parseValue = null;
		if (fieldType == String.class) {
			// String字符串
			parseValue = value;
		} else if (fieldType == char.class || fieldType == Character.class) {
			// 字符类型
			parseValue = value.charAt(0);
		} else if (fieldType == int.class || fieldType == Integer.class) {
			// int 类型
			parseValue = Integer.parseInt(value);
		} else {
			// Byte Short Long Float Double Boolean
			// 获取类型名称 得到的是完整的包名.类名 例如 java.lang.Float
			String typeName = fieldType.getName();
			
			//  例如  java.lang.Float ==> Float
			typeName = typeName.substring(typeName.lastIndexOf('.') + 1);
			
			// 拼接解析方法 parseXXX 方法名 例如  parseFloat
			String parseMethodName = "parse" + typeName;
			
			// 从当前成员变量对应数据类型中,获取 parse 系列方法
			Method parseMethod = fieldType.getMethod(parseMethodName, String.class);
			
			// 执行 parseMethod 方法 转换 value 到目标数据类型 因为 parse 系列方法都是 static 方法,静态方法执行无需对象,可以给予参数 null
			parseValue = parseMethod.invoke(null, value);
		}
		
		// 6. 成员变量对象调用 set 方法,明确给予对应类对象赋值数据
		field.set(bean, parseValue);
		
	}
}

2.8 count 计数和 forEach 最终处理
package com.qfedu.b_stream;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.stream.Stream;

public class Demo8 {
	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<String>();

		list.add("id=1,name=王某平,age=95,gender=男,score=0.5");
		list.add("id=2,name=王乾,age=99,gender=男,score=0.2");
		list.add("id=3,name=王豪豪,age=25,gender=男,score=99.5");
		list.add("id=4,name=王乐,age=16,gender=女,score=100");
		
		/*
		 * long count();
		 * 		得到当前 Stream 流中处理数据个数有多少个,一旦调用当前 Stream 终止
		 * 		再次使用会提示 异常
		 * 		Exception in thread "main" java.lang.IllegalStateException: 
		 * 				stream has already been operated upon or closed
		 */
		Stream<String> stream = list.stream();
		long count = stream.count();
		System.out.println(count);
		
		Stream<String> stream2 = list.stream();
		/*
		 * void accept(String)
		 */
		stream2.forEach(s -> {
			// 文件操作字节输出流
			FileOutputStream fos = null;
			
			try {
				// 文件路径是当前工作目录./data/student.txt 采用追加写方式
				fos = new FileOutputStream("./data/student.txt", true);

				// 字符串转换 byte[] 数组写入
				s = "[" + s + "];";
				fos.write(s.getBytes());
					
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
					
				// 关闭资源
				try {
					if (fos != null) {
						fos.close();
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

		});
	}
}
posted @ 2022-05-16 00:00  qtyanan  阅读(704)  评论(0编辑  收藏  举报