10.集合框架
所有集合类都位于 java.util 包下。Java的集合类主要由两个接口派生而出:Collection 和 Map,Collection 和 Map 是 Java 集合框架的根接口,这两个接口又包含了一些子接口或实现类。
集合框架被设计成要满足以下几个目标:
-
该框架必须是高性能的。基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的。
-
该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。
-
对一个集合的扩展和适应必须是简单的。
集合框架都包含如下内容:
-
**接口:**是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象
-
**实现(类):**是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。
-
**算法:**是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。
Collection是一个基本的集合接口,Collection中可以容纳一组集合元素(Element)
Collection 接口
Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。
List
List接口是一个有序, 元素可重复的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。
- ArrayList
底层数据结构是数组,查改快,增删慢。
非线程安全,效率高
方法:
排序
import java.util.Collections; // 引入 Collections 类Collections.sort(sites); *// 字母排序*
- Vector
底层数据结构是数组,查改快,增删慢。
线程安全,效率低
- LinkedList
底层数据结构是链表,查改慢,增删快。
非线程安全,效率高
以下情况使用 LinkedList :
-
你需要通过循环迭代来访问列表中的某些元素。
-
需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
LinkedList 继承了 AbstractSequentialList 类。
LinkedList 实现了 Queue 接口,可作为队列使用。
LinkedList 实现了 List 接口,可进行列表的相关操作。
LinkedList 实现了 Deque 接口,可作为队列使用。
LinkedList 实现了 Cloneable 接口,可实现克隆。
LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。
方法:
Set
Set 接口存储一组唯一,无序的对象。
- HashSet
底层数据结构是哈希表。(无序**,唯一**)
依赖两个方法:hashCode()和equals() 保证元素唯一性
// 引入 HashSet 类 import java.util.HashSet;public class RunoobTest { public static void main(String[] args) {
HashSet<String> sites = new HashSet<String>();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Zhihu");
sites.add("Runoob"); // 重复的元素不会被添加
System.out.println(sites);
}
}
以上代码只会输出一个Runoob。
- LinkedHashSet
底层数据结构是链表和哈希表。(FIFO插入有序,唯一)
1.由链表保证元素有序
2.由哈希表保证元素唯一
- TreeSet
底层数据结构是红黑树。(唯一,有序)
如何保证元素排序的呢? 自然排序,比较器排序
Set和List的区别
-
Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
-
Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。
-
List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector> 。
Map与Collection是并列关系。Map提供键(key)到值(value)的映射。一个Map中不能包含相同的键,每个键只能映射一个值。
- HashMap
无序,非线程安全,效率高。HashMap允许null值(key和value都允许)。
- HashTable
无序,线程安全,效率低。除构造函数外,HashTable的所有 public 方法声明中都有 synchronized关键字,而HashMap的源码中则没有。HashTable不允许null值(key和value都允许)。
- TreeMap
有序,非线程安全,效率高(O(logN)),但比不上HashMap (O(1))。
11.流(Stream)、文件(File)和IO
Java.io 包中定义了多个流类型(类或抽象类)来实现输入/输出功能;
可以从不同的角度对其进行分
类:
1.按数据流的方向不同可以分为输入流【InputStream(字节流),Reader(字符流)】和输出流【OutPutStream(字节流),Writer(字符流)】
2.按照处理数据单位不同可以分为字节流【一个字节(Byte)是8位(bit))】和字符流【一个字符是2个字节】
3.按照功能不同可以分为节点流和处理流
4.按照操作对象分
InputStream 和 OutputStream
import java.io.*;
public class fileStreamTest { public static void main(String[] args) { try { byte bWrite[] = { 11, 21, 3, 40, 5 };
OutputStream os = new FileOutputStream("test.txt"); for (int x = 0; x < bWrite.length; x++) {
os.write(bWrite[x]); // writes the bytes
}
os.close();
InputStream is = new FileInputStream("test.txt"); int size = is.available();
for (int i = 0; i < size; i++) {
System.out.print((char) is.read() + " ");
}
is.close();
} catch (IOException e) {
System.out.print("Exception");
}
}
}
上面的程序首先创建文件test.txt,并把给定的数字以二进制形式写进该文件,同时输出到控制台上。
以上代码由于是二进制写入,可能存在乱码,你可以使用以下代码实例来解决乱码问题:
//文件名 :fileStreamTest2.javaimport java.io.*;
public class fileStreamTest2 { public static void main(String[] args) throws IOException {
File f = new File("a.txt");
FileOutputStream fop = new FileOutputStream(f); // 构建FileOutputStream对象,文件不存在会自动新建
OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8"); // 构建OutputStreamWriter对象,参数可以指定编码,默认为操作系统默认编码,windows上是gbk
writer.append("中文输入"); // 写入到缓冲区
writer.append("\r\n"); // 换行
writer.append("English"); // 刷新缓存冲,写入到文件,如果下面已经没有写入的内容了,直接close也会写入
writer.close(); // 关闭写入流,同时会把缓冲区内容写入文件,所以上面的注释掉
fop.close(); // 关闭输出流,释放系统资源
FileInputStream fip = new FileInputStream(f); // 构建FileInputStream对象
InputStreamReader reader = new InputStreamReader(fip, "UTF-8"); // 构建InputStreamReader对象,编码与写入相同
StringBuffer sb = new StringBuffer(); while (reader.ready()) {
sb.append((char) reader.read()); // 转成char加到StringBuffer对象中
}
System.out.println(sb.toString());
reader.close(); // 关闭读取流
fip.close(); // 关闭输入流,释放系统资源
}
}
Reader 流与Writer流
Reader ,Write与InputStream ,OutputStream: 唯一的区别就在于读的数据单位不同分别为(16bit),(8bit)
创建读取目录:
import java.io.File;
public class CreateDir { public static void main(String[] args) {
String dirname = "/tmp/user/java/bin";
File d = new File(dirname); // 现在创建目录
d.mkdirs();
}
}
import java.io.File;
public class DirList { public static void main(String args[]) {
String dirname = "/tmp";
File f1 = new File(dirname); if (f1.isDirectory()) {
System.out.println("目录 " + dirname);
String s[] = f1.list(); for (int i = 0; i < s.length; i++) {
File f = new File(dirname + "/" + s[i]); if (f.isDirectory()) {
System.out.println(s[i] + " 是一个目录");
} else {
System.out.println(s[i] + " 是一个文件");
}
}
} else {
System.out.println(dirname + " 不是一个目录");
}
}
}
删除
import java.io.File;
public class DeleteFileDemo { public static void main(String[] args) { // 这里修改为自己的测试目录
File folder = new File("/tmp/java/");
deleteFolder(folder);
}
// 删除文件及目录
public static void deleteFolder(File folder) {
File[] files = folder.listFiles(); if (files != null) { for (File f : files) { if (f.isDirectory()) {
deleteFolder(f);
} else {
f.delete();
}
}
}
folder.delete();
}
}
缓存流
是处理流的一种,它是要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,避免频繁读写硬盘, 提高了读写的效率。同时增加了一些新的方法。
BufferedReader(Reader in)
BufferedReader(Reader in,int sz) //sz 为自定义缓冲区的大小BufferedWriter(Writer out)
BufferedWriter(Writer out,int sz)
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in,int size)
BufferedOutputStream(InputStream in)
BufferedOutputStream(InputStream in,int size)
BufferedInputStream
package com.kuang.chapter;import java.io.*;public class TestBufferStream {public static void main(String args[]) {
FileInputStream fis = null;
File f = new File("a.txt");try {
fis = new FileInputStream( f);// 在FileInputStream节点流的外面套接一层处理流BufferedInputStreamBufferedInputStream bis = new BufferedInputStream(fis);int c = 0;
System.out.println((char) bis.read());
System.out.println((char) bis.read());
bis.mark(100);// 在第100个字符处做一个标记for (int i = 0; i <= 10 && (c = bis.read()) != -1; i++) {
System.out.print((char) c);
}
System.out.println();
bis.reset();// 重新回到原来标记的地方for (int i = 0; i <= 10 && (c = bis.read()) != -1; i++) {
System.out.print((char) c);
}
bis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
BufferedReader
package com.kuang.chapter;import java.io.*;public class TestBufferStream{public static void main(String args[]){try{
BufferedWriter bw = new BufferedWriter(new FileWriter("a\\Student.txt"));//在节点流FileWriter的外面再套一层处理流BufferedWriterString s = null;for(int i=0;i<100;i++){
s = String.valueOf(Math.random());//“Math.random()”将会生成一系列介于0~1之间的随机数。// static String valueOf(double d)这个valueOf()方法的作用就是把一个double类型的数转换成字符串//valueOf()是一个静态方法,所以可以使用“类型.静态方法名”的形式来调用bw.write(s);//把随机数字符串写入到指定文件中bw.newLine();//调用newLine()方法使得每写入一个随机数就换行显示}
bw.flush();//调用flush()方法清空缓冲区BufferedReader br = new BufferedReader(new FileReader("a:\\Student.txt"));//在节点流FileReader的外面再套一层处理流BufferedReaderwhile((s = br.readLine())!=null){//使用BufferedReader处理流里面提供String readLine()方法读取文件中的数据时是一行一行读取的//循环结束的条件就是使用readLine()方法读取数据返回的字符串为空值后则表示已经读取到文件的末尾了。
System.out.println(s);
}
bw.close();
br.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
转换流
InputStreamReader 和 OutputStreamWriter 用于字节数据到字符数据之间的转换
InputStreamReader 需要和 InputStream “套接” 。
OutputStreamWriter 需要和 OutputStream “套接” 。
转换流在构造时可以指定其编码集合
import java.io.*;public class TestTransform1 {public static void main(String args[]) {try {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:/char.txt"));
osw.write("熊方园真烦人");// 把字符串写入到指定的文件中去
System.out.println(osw.getEncoding());// 使用getEncoding()方法取得当前系统的默认字符编码
osw.close();
osw = new OutputStreamWriter(new FileOutputStream("D:\\java\\char.txt", true), "utf-8");// 如果在调用FileOutputStream的构造方法时没有加入true,那么新加入的字符 串就会替换掉原来写入的字符串,在调用构造方法时指定了字符的编码
osw.write("不想搭理她");// 再次向指定的文件写入字符串,新写入的字符串加入到原来字符串的后面
System.out.println(osw.getEncoding());
osw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
数据流
数据流 DataInputStream DataOutputStream 【分别继承自InputStream 和 OutputStream】等-提供将基础数据类型写入到文件中,或者读取出来.提供了可以存取与机器无关的Java原始类型数据(int,double等)的方法。
public static void main(String args[]){
ByteArrayOutputStream baos = new ByteArrayOutputStream();//在调用构造方法时,首先会在内存里面创建一个ByteArray字节数组DataOutputStream dos = new DataOutputStream(baos);//在输出流的外面套上一层数据流,用来处理int,double类型的数try{
dos.writeDouble(Math.random());//把产生的随机数直接写入到字节数组
ByteArray中
dos.writeBoolean(true);//布尔类型的数据在内存中就只占一个字节
ByteArrayInputStream bais = new
ByteArrayInputStream(baos.toByteArray());
System.out.println(bais.available());
DataInputStream dis = new DataInputStream(bais);
System.out.println(dis.readDouble());//先写进去的就先读出来,调用readDouble()方法读取出写入的随机数
System.out.println(dis.readBoolean());//后写进去的就后读出来,这里面的读取顺序不能更改位置,否则会打印出不正确的结果
dos.close();
bais.close();
}catch(Exception e){
e.printStackTrace();
}
}
打印流
打印流是输出信息最方便的类,注意包含字节打印流PrintStream和字符打印流:PrintWriter。打印流提供了非常方便的打印功能,
可以打印任何类型的数据信息,例如:小数,整数,字符串。
对象流
对象的输入输出流的作用: 用于写入对象 的信息和读取对象的信息。 使得对象持久化。
ObjectInputStream : 对象输入流
ObjectOutPutStream :对象输出流
import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;//创建要写入磁盘的类,这个类需要实现接口 Serializable(可系列化的)class Student implements Serializable{ // 在这里保证了serialVersionUID 的唯一性,防止属性变量的临时改变,从而造成写入id与读取id不同
private static final long serialVersionUID = 1L; int id ; //额外需要添加一个属性
String name ; transient String sex; //transient修饰属性,表示暂时的,则这个属性不会被写入磁盘
transient int age; public Student(String name,String sex,int age){ this.name = name; this.sex = sex; this.age = age;
}
}public class objectIO { /**
* @param args
* @throws IOException
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws IOException, ClassNotFoundException { // TODO Auto-generated method stub
createObj();
readObj();
} //(一)先写入对象
public static void createObj() throws IOException { //1.创建目标路径
File file = new File("C:\\Users\\bg\\Desktop\\objTest.txt"); //2.创建流通道
FileOutputStream fos = new FileOutputStream(file); //3.创建对象输出流
ObjectOutputStream objOP = new ObjectOutputStream(fos); //4.创建类对象,并初始化
Student stu = new Student("玛丽苏", "男", 18); //5.向目标路径文件写入对象
objOP.writeObject(stu); //6.关闭资源
objOP.close();
} //再读取对象
public static void readObj() throws IOException, ClassNotFoundException {
File file = new File("C:\\Users\\bg\\Desktop\\objTest.txt");
FileInputStream fis = new FileInputStream(file);
ObjectInputStream objIP = new ObjectInputStream(fis); //读取对象数据,需要将对象流强制转换为 要写入对象的类型
Student stu = (Student)objIP.readObject();
System.out.println("\n name:"+stu.name+"\n sex:"+stu.sex+"\n age:"+stu.age);
objIP.close();
}
}
流的关闭顺序
-
一般情况下是:先打开的后关闭,后打开的先关闭
-
另一种情况:看依赖关系,如果流a依赖流b,应该先关闭流a,再关闭流b。例如,处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b
-
可以只关闭处理流,不用关闭节点流。处理流关闭的时候,会调用其处理的节点流的关闭方法。