018.day18 map集合如何实现排序 File类 IO流 字节流 字符流 编码

文件操作和IO流

复习

一、map集合如何实现排序

// TODO HashMap - key为一个引用类型(自定义类型)
		// 定义Hash结构的Map集合
		Map<Student, Integer> student = new HashMap<>();
		// 根据存放顺序有序
		student.put(new Student("sand", 18), 5);
		student.put(new Student("tom", 19), 2);
		student.put(new Student("tom", 19), 3);
		student.put(new Student("jerry", 20), 4);
		for (Map.Entry<Student, Integer> temp : student.entrySet()) {
			System.out.println(temp);
		}
//Student [name=tom, age=19]=3
//Student [name=jerry, age=20]=4
//Student [name=sand, age=18]=5
		// 将集合当中的元素重新排序
		// 将一个Map结合中的键值对作为ArrayList的元素,直接转换为ArrayList
		ArrayList<Map.Entry<Student, Integer>> list = new ArrayList<>(student.entrySet());
		// list集合中存储顺序与map集合一致
		for (Entry<Student, Integer> entry : list) {
			System.out.println(entry);
		}
//Student [name=tom, age=19]=3
//Student [name=jerry, age=20]=4
//Student [name=sand, age=18]=5
		// 借助Collections工具类中的sort方法
		// 第一个参数为List接口的实现类
		// 第二个参数为匿名内部类实现的比较器,或者是实现了外部比较器接口的类的实例
		Collections.sort(list, new StudentComparator());
		// 当前的排序结果记录在list当中
		for (Entry<Student, Integer> entry : list) {
			System.out.println(entry);
		}
//此时已经实现了排序,排序规则是在StudentComparator()中根据student类年龄升序排序
//Student [name=sand, age=18]=5
//Student [name=tom, age=19]=3
//Student [name=jerry, age=20]=4
public class StudentComparator implements Comparator<Entry<Student, Integer>> {
	// 指定比较器的泛型为Map.Entry<Student, Integer>
	@Override
	public int compare(Entry<Student, Integer> o1, Entry<Student, Integer> o2) {
		// o1代表当前对象,o2代表已经存在的对象
		// Integer当中已经实现了自然排序接口,默认升序
		// value中记录的是元素的存入顺序
		// 可以直接调用compareTo方法使得集合按照map当中的value升序
		// return o1.getValue().compareTo(o2.getValue());
		// 针对key进行排序
		// 如果key是一个自定义类型的实例,也可以在类的定义结构中实现自然排序接口,编写相应的比较规则,直接调用
		return o1.getKey().compareTo(o2.getKey());
	}

}
public int compareTo(Student o) {
		// 需求:向TreeSet集合当中存放Student对象,使得对象之间根据年龄升序
		// 当两个对象属性完全相同时,认定是重复对象,返回0
		Student student = null;
		// 1.判断实例的类型
		if (o instanceof Student) {
			student = (Student) o;
		}else {
			// 如果待比较元素不是Student类型,不予添加
			return 0;
		}
		if (this.name.equals(student.getName()) && this.age == student.getAge()) {
			// 当两个比较对象各属性相同时,认定相同,不予添加
			return 0;
		}
		// 2.年龄的比较
		if (this.age >= student.getAge()) {
			// 通过正负来决定位置
			// 正数放后
			// 负数放前
			return 1;
		}else {
			return -1;
		}
	}
  • 练习
// TODO HashMap练习:1.根据商品价格升序/降序 2.根据存放顺序
		Map<Product, Integer> map = new HashMap<>();
		// 0.准备数据,构建集合
		map.put(new Product("生活用品", 200), 1);
		map.put(new Product("电子产品", 300), 2);
		map.put(new Product("日常用品", 150), 3);
		map.put(new Product("食品", 400), 4);
		// 1.将集合转换为ArrayList
		// 泛型与定义保持一致
		ArrayList<Map.Entry<Product, Integer>> list = new ArrayList<>(map.entrySet());
		// 2.自定义比较器,以一个实例的方式传入
		Collections.sort(list, new ProductComparator());
		// 3.输出结果
		for (Map.Entry<Product, Integer> entry : list) {
			System.out.println(entry);
		}
		
		Collections.sort(list, new Comparator<Map.Entry<Product, Integer>>() {
			// value当中记录了存放顺序
			// Integer实现类自然排序接口
			// 默认升序与value的顺序一致
			@Override
			public int compare(Entry<Product, Integer> o1, Entry<Product, Integer> o2) {
				// 获得值
				return o1.getValue().compareTo(o2.getValue());
			}
		});
		System.out.println();
		// 3.输出结果
		for (Map.Entry<Product, Integer> entry : list) {
			System.out.println(entry);
		}
  • 数据分析的helloword
// TODO 单词计数:统计每个单词出现的总次数
		// 0.数据准备
		String[] lines = new String[4];
		lines[0] = "good good study";
		lines[1] = "day day up";
		lines[2] = "I am sand";
		lines[3] = "I am a boy good";
		// 1.遍历数据 -> 获取到每一个单词
		Map<String, Integer> result = new HashMap<>();
		for (String line : lines) {
			for (String word : line.split(" ")) {
				// word -> 获取到的每一个单词
				// 2.当第一次添加时,次数记为1;后续添加时,累加次数
				// result.get(word) == null
				// 当集合当中不包含相应的key(统计的单词) -> 第一次添加进来 -> 次数记为1
				if (!result.containsKey(word)) {
					result.put(word, 1);
				}else {
					// 非首次添加,将次数进行累加
					// int count = result.get(word) + 1;
					// result.put(word,count)
					result.put(word, result.get(word) + 1);
				}
			}
		}
		// 3.输出结果
		for (Map.Entry<String, Integer> temp : result.entrySet()) {
			System.out.println(temp);
		}

本节任务

I/O流

File类的使用

字节流和字符流

教学目标

了解I/O流的概念

掌握File类的使用

掌握字节流的使用

掌握字符流的使用

教学内容

一、File类

在Java中,使用File类对磁盘上的文件(夹)进行操作

1. 构造方法

2. 字段

  • pathSeparator:与系统有关的路径分隔符,Windows下为分号,Linux下为冒号
  • pathSeparatorChar:同上,以一个字符的形式存在
  • separator:与系统有关的名称分隔符,Windows下为反斜杠,Linux下为斜杠
  • separatorChar:同上,以一个字符的形式存在

3. 方法

// TODO File类-文件路径
		// 创建File的实例,其中包含了路径信息
		// File的构造器,以字符串的形式指定路径
		File file1 = new File("E://test");
		// 文件(文件夹)的绝对路径
		System.out.println(file1.getAbsolutePath());//E:	est
		// 文件(文件夹)的标准路径
		System.out.println(file1.getCanonicalPath());//E:	est
		// "" -> 空字符串时能够代表当前路径 -> 可以获得绝对路径 -> 不能执行文件相关操作
		// "文件名/路径" -> 从当前路径开始,拼接当前路径,获得一个完整的路径
		// "/" -> 斜杠:当前能够到达的根目录 (Windows:所在盘符/Linux:根路径)
		// 使用斜杠时以程序执行的位置有关,会获取当前所在盘符
// TODO File类-构造器
		File parent = new File("E://test");
		// 直接传入File对象作为父级路径
		File child1 = new File(parent, "b");
		System.out.println(parent.getAbsolutePath());
		System.out.println(child1.getAbsolutePath());
		// 直接传入父级路径的字符串
		File child2 = new File(parent.getPath(), "a");
		System.out.println(child2.getAbsolutePath());
// TODO File类 - 文件查看 - 练习:输出某一路径下的所有文件(夹)
		File file = new File("E://test");
		// 判断路径是否存在
		if (!file.exists()) {
			// 不存在则创建一个文件夹,返回值为boolean类型
			file.mkdir();
		}
		// 获取当前路径下的所有文件(夹)
		getFiles(file);
	}
	
	/**
	 * 递归的方式获得某一路径下的所有文件(夹)信息
	 * @param file 指定某一个存在的路径
	 */
	public static void getFiles(File file) {
		// 完全打印当前目录下的文件夹的内容
		for (File child : file.listFiles()) {
			// 打印当前目录下的子文件(夹)
			System.out.println(child);
		}
		for (File child : file.listFiles()) {
			// 判断该路径文件是否是一个文件夹
			if (child.isDirectory()) {
				// 如果子目录是一个文件夹,把其当成一个新的目录,遍历里面的子目录
				getFiles(child);
			}
		}
// TODO 路径相关的分隔符
		// 各级路径之间的分隔符(文件(夹)与文件(夹)之间的分隔符)
		// Windows下可以使用/(斜杠)和(反斜杠)
		// Linux下只能使用斜杠
		// 解决跨平台下的路径的兼容问题
		System.out.println(File.separator);
		// 路径与路径之间的分隔符
		System.out.println(File.pathSeparator);
// TODO File的删除 - 非空目录
{
    File file = new File("E:/test");
		if (!file.exists()) {
			file.mkdirs();
		}
		delete(file);
}
public static void delete(File file) {
		// 如果是空文件夹则直接删除
		if (file.delete()) {
			return;
		}else {
			// 非空文件夹
			for (File child : file.listFiles()) {
				// 尝试删除文件及文件夹
				// 对于非空文件夹会进入if结构
				// 即使不触发if,删除行为也会进行,例如:删除文件时删除成功,此时if中的值为false
				if (!child.delete()) {
					delete(child);
				}
			}
			// 删除所传入路径的最外层文件夹
			file.delete();
		}
// TODO File类的方法
		File file = new File("E:/test");
		System.out.println(file.canExecute());//执行:进入文件夹
		System.out.println(file.canRead());//读->查看文件夹中的信息
		System.out.println(file.canWrite());//写:修改文件夹内容
		// 指定的路径文件不存在时,可以创建一个新的文件
		File file2 = new File(file,"aa.txt");
		file2.createNewFile();
		// 创建所有不存在的父级目录
		File file3 = new File(file,"qq/ww/ee");
		file3.mkdirs();
		// getParentFile() -> 父级路径的File对象,以最后一级目录作为当前路径,返回剩余的路径信息
		System.out.println(file3.getParentFile().getAbsolutePath());//E:	estqqww
		File file4 = new File("E:/");
		// 当前路径已经在根目录,则返回null(返回空对象)
		// getParent -> 获取父级路径的字符串
		System.out.println(file4.getParentFile());//null

二、IO流

I:input,O:output,通常指对磁盘文件内容的读写操作,通过一个媒介或管道将文件与内存进行交互,这个媒介就被成为I/O流,流的本质为数据传输

1. 按流向分类

  • 输入流:将数据读取到程序(内存)中使用的流
  • 输出流:从程序(内存)向外输出数据使用的流

2. 按单位分类

  • 字符流:一次传输一个字符的数据,将数据以字符的形式传输
  • 字节流:一次传输一个字节的数据,将数据以字节的形式传输

3. 按层次分类

  • 节点流:可以从/向一个特定的节点读写数据的流
  • 处理流:对已存在的流的封装,通过封装的流的功能调用实现数据读写

三、字节流

1. 字节输入流

InputStream是一个抽象类,不能直接实例化对象

  • 相关方法

// TODO 字节流 - 读取文件(输入流)
		// 借助工具类 -> 每次读取若干个字节(一个或多个) -> 将读取到的数据转换为char类型输出原信息
		// 中文字符需要编码处理
		// 1.指定一个要读取的文件路径
		File file = new File("E:/test.txt");
		// 2.初始化相关的对象
		InputStream inputStream = new FileInputStream(file);
		int i = -1;
		String result = "";
		// 3.读取文件中所有的内容 - read()方法
		// 赋值的同时进行判断 -> 将获取到的值记录在某一个变量中,判断时直接使用变量值判断,同时变量值可以正常使用
		while((i = inputStream.read()) != -1) {
			result += (char)i;
		}
		// 4.将读取到的信息输出
		System.out.println(new String(result.getBytes("iso-8859-1"),"UTF-8"));
		inputStream.close();

2. 字节输出流

OutputStream是抽象类,不能直接实例化对象

// TODO 字节输出流
		// 输出到磁盘的文件中
		// 输出流重要参数:是否续写(apped),默认为flase
		// 1.指定文件路径
		File file = new File("E:/test.txt");
		// 2.初始化字节输出流
		OutputStream outputStream = new FileOutputStream(file,true);
		// 
 -> 在Windows中使用
作为换行符,Linux中使用

		// 3.向路径中写入信息(以byte形式)
		outputStream.write("
789续写".getBytes());
		outputStream.close();
  • 小练习

    • 字节流文件复制

      // TODO 字节流文件复制
      public static void main(String[] args) throws IOException {
      		copy("f:/test.txt", "f:/aa/testCopy.txt");
      	}
      
      	/**
      	 * 字节流文件复制
      	 * 
      	 * @param src
      	 *            源文件路径
      	 * @param dest
      	 *            目标文件路径
      	 */
      	public static void copy(String src, String dest) {
      		// 1.使用File指定文件路径
      		File srcFile = new File(src);
      		File destFile = new File(dest);
      		// 2.文件校验
      		// 源文件不存在,方法直接结束
      		if (!srcFile.exists()) {
      			System.out.println("源文件不存在");
      			return;
      		}
      		// 目标文件路径父级路径不存在
      		if (!destFile.getParentFile().exists()) {
      			destFile.getParentFile().mkdirs();
      		}
      		// 目标文件不存在则创建
      		if (!destFile.exists()) {
      			try {
      				destFile.createNewFile();
      			} catch (IOException e) {
      				e.printStackTrace();
      			}
      		}
      		// 3.初始化输入输出流
      		InputStream inputStream = null;
      		OutputStream outputStream = null;
      		try {
      			inputStream = new FileInputStream(srcFile);
      			outputStream = new FileOutputStream(destFile,false);
      		} catch (FileNotFoundException e) {
      			// TODO Auto-generated catch block
      			e.printStackTrace();
      		}
      		// 4.文件复制
      		// length代表本次读取到的字节数,-1时代表到达文件末尾
      		int length = -1;
      		// 将每次读取到的数据放入byte数组当中
      		// 使用字节数组的方式可以提高读写效率
      		byte[] data = new byte[1024];
      		try {
      			while ((length = inputStream.read(data)) != -1) {
      				// 写入byte数组中的数据,同时指定偏移量,从数组开头,一直到当前从源文件中读取的数据长度
      				outputStream.write(data,0,length);
      			}
      			// 5.关闭输入输出流
      			inputStream.close();
      			outputStream.close();
      		} catch (IOException e) {
      			e.printStackTrace();
      		}
      	}
      
    • 字节流练习:文件内容比较

      public static void main(String[] args) {
      		// TODO 字节流练习:文件内容比较
      		System.out.println(compare("E:/test.txt", "E:/testCopy.txt"));
      	}
      	
      	/**
      	 * 比较两个文件的内容是否完全一致
      	 * @param c1 比较文件A
      	 * @param c2 比较文件B
      	 * @return 两个文件是否相同
      	 */
      	public static boolean compare(String c1,String c2) {
      		// 1.使用File指定文件路径
      		File cFile1 = new File(c1);
      		File cFile2 = new File(c2);
      		// 2.文件校验
      		if (!cFile1.exists() || !cFile2.exists()) {
      			System.out.println("请检查源文件路径");
      			return false;
      		}
      		// 3.初始化输入流
      		InputStream inputStreamC1 = null;
      		InputStream inputStreamC2 = null;
      		try {
      			inputStreamC1 = new FileInputStream(cFile1);
      			inputStreamC2 = new FileInputStream(cFile2);
      		} catch (FileNotFoundException e) {
      			// TODO Auto-generated catch block
      			e.printStackTrace();
      		}
      		// 4.文件内容比较
      		int i = -1;
      		int j = -1;
      		// 结果初始值为true
      		boolean result = true;
      		try {
      			// 每次用i和j记录读取到的数据,当达到文件末尾时,值为-1
      			// &&/||逻辑运算有时会出现短路,当第一个结果已经能够决定整个表达式运算结果时,第二个表达式不会执行
      			// 可以使用位运算符
      			while ((i = inputStreamC1.read()) != -1 | (j = inputStreamC2.read()) != -1) {
      				// 每次读取到的字符进行比较
      				if (i != j) {
      					// 如果出现不相等,两个文件不相同,直接跳出
      					result = false;
      					break;
      				}
      			}
      			// 当某一个文件提前结束,i和j当中一定有一个为-1
      			// 此时i和j不相等
      			/*if (i != j) {
      				result = false;
      			}*/
      		} catch (IOException e) {
      			e.printStackTrace();
      		}
      		return result;
      	}
      

3. 文件输入流

以字节流的形式读取一个文件中的数据

  • FileInputStream:使用File作为参数初始化,指定要读取的文件

  • read()方法:每次读取一个字节

    // TODO 字符流输入流
    		// 1.初始化字符流
    		// 需要一个InputStream -> 实现类
    		Reader reader = new InputStreamReader(new FileInputStream(new File("f:/test.txt")));
    		// 2.读取文件中的数据
    		int i = -1;
    		while ((i = reader.read()) != -1) {
    			System.out.print((char)i);
    		}
    		// 3.关闭流
    		reader.close();
    
// TODO 字符流一次读入多个字符
		// 1.初始化字符流
		// 需要一个InputStream -> 实现类
		Reader reader = new InputStreamReader(new FileInputStream(new File("f:/test.txt")));
		// 2.读取文件中的数据
		// 当前次读取到的字符个数
		int length = -1;
		// 每次读取的最大字符数
		char[] cs = new char[5];
		while ((length = reader.read(cs)) != -1) {
			// 最后一次(临近文件末尾)不一定会装满字符数组,此时以length为准
			for (int j = 0; j < length; j++) {
				System.out.print((char) cs[j]);
			}
		}
		// 3.关闭流
		reader.close();

4. 文件输出流

  • FileOutputStream:使用File作为参数初始化,指定要写入的文件
  • write(byte[] b)方法:从指定byte数组中将数据写入此文件输出流中
// TODO 字符流的输出流
		// 1.初始化字符输出流
		Writer writer = new OutputStreamWriter(new FileOutputStream(new File("E:/test.txt"),true));
		// 2.使用字符流的方式写入数据
		writer.write("!字符流输出!");
		// 3.使用flush方法将缓冲区的数据写入文件
		writer.flush();
		// 流关闭时也会释放缓冲区
		// 4.关闭流
		writer.close();
  • 小练习
// TODO 使用字符流实现文件的复制
		copy("E:/test.txt", "E:/aa/bb/testCopy.txt");
	}

	public static void copy(String src,String dest) {
		// 1.初始化文件路径
		File srcFile = new File(src);
		File destFile = new File(dest);
		// 2.路径校验
		if (!srcFile.exists()) {
			System.out.println("源文件不存在");
			return;
		}
		if (!destFile.getParentFile().exists()) {
			// 自动创建父级目录
			destFile.getParentFile().mkdirs();
		}
		// 3.初始化需要的流
		Reader reader = null;
		Writer writer = null;
		try {
			reader = new InputStreamReader(new FileInputStream(srcFile));
			writer = new OutputStreamWriter(new FileOutputStream(destFile));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 4.字符流方式文件复制
		int length = -1;
		char[] data = new char[5];
		try {
			while ((length = reader.read(data)) != -1) {
				writer.write(data, 0, length);
				writer.flush();
			}
			// 5.关闭输入输出流
			reader.close();
			writer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

四、字符流

1. 输入流

抽象父类:Reader

  • 子类:InputStreamReader,FileReader
  • 初始化字符输入流:需要一个文件流
  • 初始化文件流:需要一个File对象(包含路径信息)
// 细化写法
File file = new File("E:/test.txt");
InputStream inputStream = new FileInputStream(file);
Reader reader = new InputStreamReader(inputStream);
// 简化写法
Reader reader = new InputStreamReader(new FileInputStream(new File("E:/test.txt")));
  • read():每次读取一个字符(中英文均可),读取到文件末尾返回-1
  • 可以一次读取多个字符

2. 字符输出流

2. 输出流

抽象父类:Writer

  • 子类:OutputStreamWriter,FileWriter
  • 初始化字符输出流:需要一个文件流
  • 初始化文件流:需要一个File对象(包含路径信息)
// 细化写法
File file = new File("E:/test.txt"); 
OutputStream inputStream = new FileOutputStream(file); 
Reader reader = new OutputStreamWriter(inputStream);
// 简化写法
Writer writer = new OutputStreamWriter(new FileOutputStream(new File("E:/test.txt")));
  • write(String str):可以直接向目标写入字符串
  • flush():刷新缓冲,对于有缓冲区的流都应调用该方法

五、编码

文本内容必须经过正确的编码和解码才能够正常显示,但有些时候我们可以通过ISO-8859-1编码来获得一个正确的表示

  • 字符串编码:getBytes(String charsetName)方法获得一个byte数组
  • 解码显示:new String(byte[] bytes,charsetName)
  • 解码方式和编码方式相同时,可以正常显示
  • UTF-8以及GBK格式经过ISO-8859-1解码再编码后,数据不丢失
  • UTF-8格式经过GBK解码再编码后,数据不丢失
原文地址:https://www.cnblogs.com/yokii/p/9441840.html