Java-----集合,IO流
文章目录
1.集合(java.util.*)
(1).概述
Ⅰ.集合实际上是一个容器(载体),可存储多个对象;(数组是最简单的集合);
Ⅱ.集合中不能存储基本数据类型和java对象,存储的是java对象的内存地址;
Ⅲ.每个不同的集合底层对应不同的数据结构(数组,二叉树,链表,哈希表…);
(2).集合的分类和结构图
<1>.Collection接口:
超级父接口:java.util.Collection
a.Collection继承结构图:
概述:
①.Collection可存放Object的所有子类对象;
②.由于Collection是接口,无法实例化,new对象应用多态机制创建子类的实现类对象。
b.常用方法
A.向集合中添加一个元素
//向集合中添加一个元素
public boolean add(Object e);
B.获取集合中的元素个数
//获取集合中元素的个数
public int size();
C.清空集合中的元素
//清空集合中的元素
public void clear();
D.判断集合中是否包含某元素
Tips:
该方法中底层调用了equals方法,放在集合中的元素应重写equals方法。
//判断集合中是否包含某元素
public boolean contains(Object e);
E.移除集合中的某元素
//移除集合中的某元素
public boolean remove(Object e);
F.判断集合是否为空
//判断集合是否为空
public boolean isEmpty();
G.将集合转换为数组
//将集合转换为数组
public Object[] toArray();
<2>.Iterator迭代器接口:
a.获取迭代器对象:
Collection c = new ArrayList();
Iterator it = c.iterator();
//iterator()方法是Collection接口的父接口Iterable中的方法
b.迭代器中的方法:
A.判断集合是否可以迭代
//判断集合是否可以迭代
public boolean hasNext();
B.返回迭代的下一个元素
//返回迭代的下一个元素
public Object next();
C.删除当前指向的元素
//删除当前指向的元素
public void remove();
c.使用迭代器需要注意的:
A.对迭代器来说,当集合结构发生改变时,迭代器必须重新获取;
B.采用迭代器的remove()方法删除可自动更新迭代器。
<3>.List接口:存储元素有下标,有序可重复
a.常用方法:
A.在指定位置i处添加元素
//在指定位置i处添加元素
public void add(int index,Object element);
B.根据下标获取元素
//根据下标获取元素
public Object get(int index);
C.获取指定对象第一次出现的索引
//获取指定对象第一次出现的索引
public int indexOf(Object element);
D.获取指定对象最后一次出现的索引
//获取指定对象最后一次出现的索引
public int lastIndexOf(Object element);
E.删除指定位置的元素
//删除指定位置的元素
public void remove(int index);
F.修改指定位置的元素值
//修改指定位置的元素值
public void set(int index,Object Element);
b.ArrayList类详解:
A.初始容量
ArrayList集合底层由数组实现,初始容量为10,也可通过构造方法new ArrayList(int capacity);
指定初始化容量。
B.扩容
当存储空间不够时,该集合自动扩容1.5倍。
本质上是位运算符二进制右移一位1010>>1=0101—>5
,左移同理。
C.优缺点
优点:检索效率较高,末尾加元素效率较高;
缺点:增删效率较低。
c.LinkedList类详解:
A.链表数据结构
结点Node是链表中的基本单元。
单向链表:
每一个Node中有两个属性:存储的数据和下一个结点的内存地址。
双向链表:
每一个Node中有三个属性:存储的数据,下一个和上一个结点内存地址。
B.优缺点
优点:随机增删元素的效率较高;
缺点:查询效率较低,查找元素需从头开始向下遍历。
d.Vector类详解:
A.初始容量
Vector集合底层由数组实现,初始化容量是10,也可通过构造方法new Vector(int initialCapacity);
设置初始化容量。
B.扩容
Vector存储空间用满时,自动扩容2倍。
C.方法特征
Vector集合中的方法都是线程安全的(带有synchronized关键字)。
e.泛型机制:
A.作用
规定集合中存储的元素类型固定,集合中数据类型更统一。
B.语法机制
//规定List方法中仅能存储String类型的数据 例子
//方法一:
List<String> l = new ArrayList<>();
//方法二:
List l = new ArrayList<String>();
//方法三:
List<String> l = new ArrayList();
C.优缺点
优点:
①集合中存储的元素类型统一了;
②从集合中取出的数据是泛型指定类型,无需更多的向下转型。
缺点:
导致集合中的元素类型缺乏多样性。
f.总结集合的迭代与遍历
//创建集合以及添加元素
List<String> l1 = new ArrayList <>();
l1.add(0,"香辣牛肉月饼");
l1.add(1,"莲蓉月饼");
l1.add(2,"五仁月饼");
A.迭代器遍历
//迭代器遍历集合
Iterator <String> it = l1.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
B.采用List集合的下标
//下标遍历
for (int i = 0;i < l1.size();i++){
System.out.println(l1.get(i));
}
C.采用增强for进行遍历
//foreach遍历
for (String s:l1){
System.out.println(s);
}
<4>Map接口:元素无下标,无序不可重复
超级父接口:java.util.Map
a.数据存储特点
Map集合以key和value的方式存储数据,即【键值对】方式。
key和value都是引用数据类型,都存对象的内存地址。
Map继承结构图
c.常用方法
A.向Map集合中添加键值对Map<K,V> map = new TreeMap<>();
//向Map集合中添加键值对
public V put(K key,V value);
B.通过key值来获取value值
//通过key值来获取value值
public V get(K key);
C.清空Map集合
//清空Map集合
public void clear();
D.判断Map集合中是否包括某个key或者value
//判断Map集合中是否包括某个key或者value
public boolean containsValue(Object value);
public boolean containsKey(Object key);
E.判断Map集合是否为空
//判断Map集合是否为空
public boolean isEmpty();
F.获取Map集合中所有的Key存储至一个Set集合中
//获取Map集合中所有的Key存储至一个Set集合中
public Set<K> keySet();
G.通过key值删除键值对
//通过key值删除键值对
public V remove(Object key);
H.获取Map集合中键值对的个数
//获取Map集合中键值对的个数
public int size();
I.获取Map中所有的Value存储到一个Collection中
//获取Map中所有的Value存储到一个Collection中
public Collection<V> values();
J.将Map集合转换成Set集合
//将Map集合转换成Set集合
public Set<Map.Entry<K,V>> entrySet();
d.Map集合的遍历方法
//创建集合以及添加元素
TreeMap map = new TreeMap<Integer,String>();
map.put(1,"汉堡包");
map.put(2,"炸薯条");
map.put(3,"香辣鸡翅");
A.获取所有的Key,通过get方法和迭代器遍历
//获取所有的key,再通过get方法得到value值
Set<Integer> set = map.keySet();
Iterator <Integer> it = set.iterator();
while(it.hasNext()){
Integer key = it.next();
System.out.println(key+"="+map.get(key));
}
B.通过entrySet方法将键值对转换为Set集合,再迭代器遍历
//通过entrySet将map转换为set集合,再迭代器遍历
Set<Integer> set = map.entrySet();
Iterator <Integer> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
e.HashMap类详解:
A.哈希表数据结构概述
定义:
哈希表是一个数组和单向链表的结合体,综合了以上二者的优点。
哈希下标:
①.同一个单向链表上的结点哈希值相同,因为其数组下标相同;
②.HashMap集合的key使用时应重写hashCode()
和equals()
方法。
put(K key,V value)方法实现原理:
①.封装key,value到Node对象中;
②.调用key的hashCode()方法得出Hash值。
③.通过哈希算法将Hash值转换为数组下标。
若下标位置无元素,则将Node添加上去;
若有元素(链表),将key与链表上每个结点key进行equals()比对:
若返回均为false,则该结点被加至末尾;
若返回存在true,则该结点的value值被覆盖。
get(K key)方法实现原理:
①.调用key的hashCode()得出哈希值;
②.通过哈希算法将哈希值转换为数组下标;
③.通过下标锁定位置。
若下标元素无元素,返回一个null;
若有元素(链表),拿参数key与单向链表上每个结点进行equals比对:
若返回均为false,则该方法返回null;
若返回存在true,则返回该结点的value值。
B.HashMap集合详解:
初始容量
HashMap集合的初始化容量为16。
也可通过new HashCode(int capacity)
进行初始化容量操作,但该集合容量必须是2的整数次幂。
(即当容量达到【16x0.75=12时】,集合自动扩容值32。)
默认加载因子
定义:当集合存储元素容量达到默认加载因子时,集合自动扩容。
HashMap:该集合的默认加载因子为0.75。
存储元素信息
HashMap集合中的【key】值可以为null,【value】值也可以为null。
f.Hashtable类详解:
A.Hashtable类概述:
初始容量
Hashtable集合的初始容量为11;
默认加载因子及扩容量
Hashtable的默认加载因子为0.75,扩容量为【原容量*2+1】。
存储元素信息
①.Hashtable集合中的【key】值和【value】值均不可为null。
②.该集合是线程安全的。
B.properties类详解:
存储数据类型:
key和value类型都应是String类型。
常用方法:
向集合中存入数据
//向集合中存入数据
public synchronized Object setProperty(String key,String value);
得到集合中的元素
//得到集合中的元素
public synchronized String getProperty(String key);
g.TreeSet和TreeMap类详解
A.概述:
①.TreeSet集合底层是个TreeMap集合,TreeMap底层是二叉树数据结构。
②.TreeSet集合中的元素相等于放入了TreeMap集合中的key部分了。
③.Map集合无序不可重复,TreeMap集合可自动排序。
B.排序规则:
①.TreeSet对自定义的类来说,TreeSet不可排序;
②.若给自定义的类添加【排序规则】,即实现Comparable接口和内部的comparedTo()方法,在其中写入【排序规则】即可。
③.TreeMap和TreeSet的排序规则采取二叉树中的【中序遍历】。
C.实现排序的两种方法:
①.实现java.util.Comparable接口,通常用于排序规则不会改变;
②.构建集合对象时传入一个比较器对象comparator接口,通常用于排序规则频繁改变。
方法一测试:
准备工作:准备一个学生类Student,让其实现Comparable接口,并重写comparedTo()方法,排序规则如下书写:
//学生作key时,先让其比较学生的学号,如果学号相等,再比较学生姓名
public int compareTo(Student o) {
if (this.no < o.no){
return -1;
}else if (this.no > o.no){
return 1;
}else{
return this.name.compareTo(o.name);
}
}
书写测试类:
public static void main(String[] args) {
Map<Student,Integer> map = new TreeMap <>();
map.put(new Student("shawn",5),5);
map.put(new Student("Alice",6),6);
map.put(new Student("James",7),7);
map.put(new Student("Jane",8),8);
map.put(new Student("Alice",7),7);
System.out.println(map);
}
测试结果与总结:
{Student{name=‘shawn’, no=5}=5,
Student{name=‘Alice’, no=6}=6,
Student{name=‘Alice’, no=7}=7,
Student{name=‘James’, no=7}=7,
Student{name=‘Jane’, no=8}=8}
总结:由上述结果可知,对于学号no较小的被排在上面,而中间学号no相等时,会比较姓名,从而达成了自定义类的排序功能。
方法二测试:
准备工作:准备一个学生类Student,让其实现Comparable接口,并重写comparedTo()方法,排序规则与上同
测试类书写:
public static void main(String[] args) {
Map<Student,Integer> map = new TreeMap <>(new Comparator <Student>() {
@Override
public int compare(Student o1, Student o2) {
if (o1.getNo()<o2.getNo()){
return -1;
}else if (o1.getNo()>o2.getNo()){
return 1;
}else{
return o1.getName().compareTo(o2.getName());
}
}
});
map.put(new Student("shawn",5),5);
map.put(new Student("Alice",6),6);
map.put(new Student("James",7),7);
map.put(new Student("Jane",8),8);
map.put(new Student("Alice",7),7);
System.out.println(map);
}
输出结果:
(与上同!)
h.集合的工具类Collections:
常用方法:
A.将集合从非线程安全变为线程安全
//将集合从非线程安全变为线程安全
public static List<T> synchronizedList(list l);
B.排序集合中的元素
//排序集合中的元素
public static List<T> sort(List l);
2.IO流(Input/Output Stream)
(1).概述
作用:
通过IO流可完成【硬盘文件的读和写】。
读和写的区分:
·读进入内存为输入【读】;
·写出来内存为输出【写】。
所在包:
java中所有的流均在java.io.*
下。
分类:
Ⅰ.
java.io.InputStream
:字节输入流;
Ⅱ.java.io.OutputStream
:字节输出流;
Ⅲ.java.io.Reader:
:字符输入流;
Ⅳ.java.io.Writer
:字符输出流。
实现的接口:
※ ①所有的【输出流】都实现了java.io.Flushable
接口,存在flush()
方法,作用是【刷新流】。
※ ②【所有流】都实现了java.io.Closeable
接口,存在close()
方法,作用是【关闭流】。
(2).文件类流详解:
分类:
Ⅰ.
java.io.FileInputStream
Ⅱ.java.io.FileOutputStream
Ⅲ.java.io.FileReader
Ⅳ.java.io.FileWriter
<1>.FileInputStream类详解:
定义:
【文件字节输出流】,可读取任意格式的文件。
构造方法:
源码:
/*
*@param name:文件名
*/
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
/*
*@param file:读取的文件对象
*/
public FileInputStream(File file);
read()方法和read(byte[] bytes)方法:
/*
*该方法一次可读取目标文件中的一个字节,效率较低。
*@return 若有读取值返回读到的字节本身,无读取值则取-1
*/
public int read();
为了减少硬盘和内存之间的交互,引入read(byte[] bytes)方法
/*
*该方法一次课读取目标文件的【bytes.length】个字节,效率较高。
*@return 返回读取到的字节数目,无读取值则返回-1
*/
public int read(byte[] b);
※ 使用read(byte[] b)方法时,输出字节本身可以使用String的构造方法
//将byte数组转换为字符串进行输出即可
new String(byte[] bytes,int offset,int readCount);
从而得出读文件的最优解:
Tips:【io测试文本】中存储为【“Hello IOStream!”】
//创建流对象:(让fis对象作用域扩展,能在finally语块中读取到该对象)
FileInputStream fis = null;
try {
//指定要读取的文件
fis = new FileInputStream("io测试文本.txt");
int readCount = 0;
byte[] bytes = new byte[4];
while((readCount = fis.read(bytes)) != -1){
System.out.print(new String(bytes,0,readCount));
}
} catch (IOException e) {
e.printStackTrace();
} finally{
//最后必须关闭流:(加入判断为了防止【空指针异常】)
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
输出结果为:
【Hello IOStream!】
其他方法:available()
//使用该方法一次读完数据
/*
*@return 返回可读取的字节数量
*/
public int available();
skip()
/*
*@param l:bytes数组需要跳过总字节数
*@return 实际跳过的字节数
*/
public long skip(long l);
<2>.FileOutputStream类详解:
定义:
【文件字节输出流】。
构造方法:
/*
*@param name:需要写入的文件名
*@param append:写入的数据是否在原有基础上拼接
*/
new FileOutputStream(String name,boolean append);
write(byte[] bytes)方法:
/*
*向原文件中写入数据
*@param bytes 写入的数据的byte数组
*/
public void wirte(byte[] bytes);
※:添加字符串时可调用String中的getByte(String s)方法将字符串转换为byte数组。
写入数据样例:
//定义输出流
FileOutputStream fos = null;
try {
fos = new FileOutputStream("io测试文本.txt",true);
String str = "我是中国人,我骄傲";
//将字符串转换为byte数组
byte[] bytes = str.getBytes();
fos.write(bytes);
//输出流需刷新流:
fos.flush();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
//结尾需要关闭流
if (fos != null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
<3>.FileReader类简介:
定义:
【文件字符输入流】,仅能读取【普通文本】。
<4>.FileWriter类简介:
定义:
【文件字符输出流】,仅能读取【普通文本】。
<5>.文件拷贝功能实现:
源代码:
package IO流;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopy {
public static void main(String[] args) {
//输入流的定义
FileInputStream fis = null;
//输出流的定义
FileOutputStream fos = null;
try {
fis = new FileInputStream("E:\\highlights\\跑跑机器人.mp4");
fos = new FileOutputStream("E:\\泡泡机器人.mp4");
//初始化计数变量readCount和数组byte[]
int readCount = 0;
byte[] bytes = new byte[1024*1024];//一次性拷贝1M文件
//一边写一边读
while((readCount = fis.read(bytes))!= -1){
fos.write(bytes,0,bytes.length);
}
//刷新输出流
fos.flush();
} catch (IOException e) {
e.printStackTrace();
}finally{
//关闭输入和输出流
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
结果:
【E:/highlights/跑跑机器人.mp4】视频文件成功被拷贝到
【E:/泡泡机器人.mp4】,在此过程中同时进行了重命名操作。
(3).缓冲区类流详解:
缓冲区类流有:
Ⅰ.
java.io.BufferedReader
Ⅱ.java.io.BufferedWriter
Ⅲ.java.io.BufferedInputStream
Ⅳ.java.io.BufferedOutputStream
BufferedReader类(BufferedWriter类同理):
定义:自带缓冲区的字符输入流;
构造方法:
new BufferedReader(Reader reader);
方法:
readLine()方法:
//一次读取文件的一行内容。
public String readLine();
read()方法:
//一次读取一个字符,返回值为ASCII值。
public int read();
read(char[] chars)方法:
//一次读取chars.length个长度,存入chars数组中,返回值为读到的字符数量
public int read(char[] chars);
转换流InputStreamReader:将字节流转换为字符流:
InputStreamReader I = new InputStreamReader(new FileInputStream("io测试文本"));
(4).标准输出流详解:
<1>.PrintStream
所在包:java.io.PrintStream
①.标准的【字节输出流】,默认输出到控制台。
②.该流无需手动关闭。
③.手动改变输出方向:(System类下)
public static void setOut(PrintStream ps);
<2>.PrintWriter
所在包:java.io.PrintWriter
①.标准的【字符输出流】,与上同;
(5).对象类流详解:
概述:
【serialize,deserialize】
①.【(反)序列化】:(反)将内存中的java对象放入硬盘文件中。
②.参与序列化与反序列化的对象,必须实现Serializable
接口。
③.在属性前加【transient】关键字,表现该变量不参与(反)序列化。
④.IDEA可自动生成序列化版本号。
⑤.Map集合中的Properties类中的load()
方法可将硬盘文件加载到内存中。
public synchronized void load(InputStream inStream);
public synchronized void load(Reader reader);
(6).java.io.File类详解:
概述:
File类不能完成对象的读和写,File是一个路径名的抽象表现形式。
构造方法:
//文件的路径名
new File(String pathname);
方法:
exists()方法:
//判断是否存在目标文件
public boolean exists()
createNewFile()方法:
//在当前目录下创建一个文件
public void createNewFile();
mkdir()方法:
//在当前目录下创建出一个子目录
public void mkdir();
getParent():
//获取文件父路径
public String getParent();
getAbsolutePath():
//获取文件的绝对路径
public String getAbsolutePath();
listFiles();
//获取当前目录下所有子文件
public File[] listFiles();
(7).拷贝目录程序设计:
/*
*该方法用于目录的拷贝
*@param srcFile:拷贝源文件对象
*@param deskFile:拷贝目标文件对象
*@param src:拷贝源文件地址
*@param desk:拷贝目标文件地址
*/
public static void copy(File srcFile,File deskFile,String src,String desk){
//列出源目录下的所有子目录:
File[] files = srcFile.listFiles();
//遍历files数组拿到所有的子目录与子文件:
//当拿到的是文件时:
if (srcFile.isFile()){
//使用FOS与FIS即可实现拷贝:
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(desk);
int readCount = 0;
byte[] bytes = new byte[1024*1024];//一次传1MB
while((readCount = fis.read(bytes))!=-1){
fos.write(bytes,0,readCount);
}
//输出流刷新:
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//输入输出流关闭:
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return;
}
//当拿到目录时:
for (File file : files){
if (file.isDirectory()){
String srcDir = file.getAbsolutePath();
String deskDir = (deskFile.getAbsolutePath().
endsWith("\\")?deskFile.getAbsolutePath()
:deskFile.getAbsolutePath()+"\\")+srcDir.substring(3);
System.out.println(srcDir);
System.out.println(deskDir);
File newFile = new File(deskDir);
if (newFile.exists()){
newFile.mkdirs();
}
}
// 递归调用
copy(file,deskFile,src,desk);
}
}
}