集合

集合和IO - 图2

概述

  1. 集合在JDK java.util.* 包下

所有集合类和集合接口都在

  1. 集合是一个载体,一个容器,一次可以容纳多个对象

  2. 集合里存的是对象的内存地址

  3. 集合本身也是一个对象(有内存地址)

  4. 集合里可以存集合

  5. 不同的集合底层对应不同的数据结构

  6. 集合中不能直接存储基本数据类型,他会自动的帮你装箱

在Java中集合分为两大类

  1. 一类是单个方式存储元素,超级父接口 java.util.Collection
  2. 一类是以键值对(key,value)的方式存储元素,超级父接口 java.util.Map

Collection

  1. Collection 是集合里面的超级父接口
  2. Collection可以存储那些元素

在没有使用泛型的情况下可以存储Object所有子类

使用了泛型,只能存储某个具体的类型

Collection 常用方法

  1. boolean add(Object e) //向集合里面添加元素(末尾)

  2. int size() //查询集合中元素的个数

  3. void clear() //清空集合

  4. boolean contains(Object o) // 判断集合中是否包含

底层调用了equals()判断,所以放在集合中的元素需要重写equals()方法

如果u2 和集合中的u1 内容完全相同,判断结果是true

  1. remove(Object o) //删除集合中某个元素

底层调用了equals()判断,所以放在集合中的元素需要重写equals()方法

如果u2 和集合中的u1 内容完全相同,删除u2,u1会被删除

  1. boolean isEmply() //判断集合是否为空 空=true 不空 =false

  2. Object[] toArray() //将集合转化成数组,返回一个Object数组

  3. Iterator iterator() //拿到迭代器 返回一个Iterator

增强for循环

用来遍历集合/遍历数组

  1. for(元素类型 变量名 数组或者集合){
  2. 循环体 //System.out.println(变量名);遍历集合
  3. }

collection 继承结构图

集合和IO - 图3

List/Set/SortedSet特点

集合和IO - 图4

集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,

集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用。)

list.add(100); //自动装箱Integer

注意:

  • 集合在java中本身是一个容器,是一个对象。
  • 集合中任何时候存储的都是“引用”。

在java中集合分为两大类:

  • 一类是单个方式存储元素:

  • 单个方式存储元素,这一类集合中超级父接口:java.util.Collection;

  • 一类是以键值对儿的方式存储元素

  • 以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;


Collection-List接口

旗下有ArrayList 、 LinkedList 、 Vector

特点

  • 有序

  • 可重复

  • 有下标 0开始 以1递增

List接口为Collection的子接口,也就是说Collection中有的方法,这里都有

创建集合 List l = new ArrayList(); 多态

List集合常用方法

  1. void add(下标,元素) //在指定的位置插入元素
  2. Object get(下标) //得到指定位置的元素
  3. int indexOf("元素") //得到该元素第一次出现的下标
  4. int lastIndexOf("元素") //得到该元素最后一次出现的下标
  5. Object remove(下标) // 删除指定位置的元素
  6. Object set(下标,元素) //修改指定下标的元素

链表

优点

随机增删元素效率较高

缺点

查询效率底

在开发中如果遇到了随机增删元素较多的业务时,建议使用LinkedList

ArrayList

  • ArrayList集合初始化容量10

  • 扩容为原容量1.5倍。

  • 底层是数组。

  • 数组优点和缺点要能够说出来!

  • 另外要注意:ArrayList集合末尾增删元素效率还是可以的。

Vector:

  • Vector初始化容量是10.

  • 扩容为原容量的2倍。

  • 底层是数组。

  • Vector底层是线程安全的。

  • 怎么得到一个线程安全的List:

  • Collections.synchronizedList(list);

集合的遍历/迭代器

此内容只能在Collection中使用Map集合不能使用

  1. Iterator i = 集合.iterator(); // 拿到迭代器

迭代器常用方法

boolean hasNext()

如果返回true表示有元素可以迭代

如果返回false表示没有元素可以迭代

Object next();

让迭代器前进一位,并且拿到该元素

迭代器对象最初没有指向任何元素

遍历集合

  1. Collection c = new ArrayList(); // 创建集合 多态
  2. Iterator i = c.iterator(); //拿到迭代器
  3. while(i.hasNext()){ //如果有元素返回true ,执行循环,没有则退出
  4. System.out.println(i.next()); // 迭代
  5. }

注意

  1. 当集合发生结构变化,一定要重新获取迭代器
  2. 在迭代元素时不能调用c.remove()方法 ,删除元素但是可以调用i.remove()方法 ,删除元素

泛型

在JDK5.0出现的新特性

List myList = new ArrayList<>(); <>钻石表达式

使用泛型后myList集合中只允许出现Animal类或者他的子类

优点

使用泛型后,集合中的数据类型更加统一

缺点

导致集合中的储存元素缺乏多样性

注意

使用泛型之后迭代器迭代的就不是Object类型了而是<> 这里面的类型


Collection-TreeSet集合/Vector集合

  1. 底层是TreeMap

  2. TreeMap底层是二叉树

  3. 放到TreeSet集合中的元素 等同于放到TreeMap的key部分

  4. 特点

无序不可重复

自动排序,从小到大

排序可以自定义

比较规则

升序 : return this.age-c.age;

降序 : return c.age - this.age;

例如

  1. TreeSet<B> treeSet2 = new TreeSet<>(new Comparator<B>() {
  2. @Override
  3. public int compare(B b, B t1) {
  4. return b.age - t1.age;
  5. } //匿名内部类
  6. });
  7. treeSet2.add(new B(520));
  8. treeSet2.add(new B(500));
  9. treeSet2.add(new B(510));
  10. treeSet2.add(new B(400));
  11. treeSet2.add(new B(300));
  12. for ( B b : treeSet2){
  13. System.out.println(b);
  14. }

结论

  1. 放到TreeSet或者TreeMap集合中key部分想要排序包括两种方式
  • 集合中的元素类中实现Comparable接口
  • 在构造的时候传一个比较器Comparator接口(对象)
  1. 如果比较规则不变使用Comparable接口
  2. 如果比较规则有多个,需要切换,用Comparator接口(匿名内部类)

Vector 集合

  • 底层是数组

  • 初始容量为10

  • 扩容之后是原容量的2倍 10—>20—>40

  • 所有的方法都是线程同步的(所以不常用)

Collection-集合继承结构图

集合和IO - 图5


Map

概念

Map集合以key和value的方式储存数据(键值对)

  1. key和value是引用数据类型

  2. key和value保存的是对象的内存地址

  3. key起主导作用,value是附属品

Map集合常用方法

  1. put(k,v) //向Map集合中添加键值对

  2. get(Object key) // 通过key找到value

  3. void clear() //清空集合

  4. boolean containsKey(Object key) // 判断集合里有没有key(只在key中找)

  5. boolean contiansValue(Object value) //判断集合中有没有value(只在value中找)

  6. boolean isEmpty() //判断集合是否为空

  7. int size() //获取键值对的数量,一组为一个

  8. remove (Object key) // 通过key来删除键值对

  9. Collection values() // 得到所有的value元素 用一个Collection 集合接收

  10. Set keySet() //得到所有的key元素 用Set集合接收

哈希表数据结构(散列表)

  1. 哈希表是数组和单向链表的结合体

  2. 哈希表就是将传进来的key元素,通过hashCode算出来一个下标,将key存进去

  3. 如果重写hashCode方法返回都是一样的值,那么就变成了纯链表

  4. 如果重写的hashCode方法返回的值都不一样,那么就变成了纯数组

  5. 如果put(k重复了,value会覆盖)

重点

  • 放在HashMap/HashSet集合里key部分的元素必须同时重写HashCode()和equals()

  • hashCode()和equals()方法不需要自己写直接idea生成

  • HashMap集合初始化容量必须是2的次幂,这也是官方推荐 【推荐16】

简易Map继承图

集合和IO - 图6

HashMap

  • HashMap集合的默认初始化容量为16,默认加载因子为0.75
  • 默认加载因子是指HashMap集合底层数组的容量达到75%以后开始扩容

Properties集合

继承Hashtable

key和value都只能存String

属性类对象(线程安全)

方法()

setProperty(k,v) //存元素

getProperty(k); //通过key得到value

自平衡二叉树

遵循左小右大的原则存放

遍历

前序遍历 根左右

中序遍历 左根右

后序遍历 左右根

集合工具类Collections

java.util.Collections

Collentions.synchronizedList(); //静态方法,变成线程安全的

Collentions.sort(集合名); // 静态方法排序

(必须是list集合 前提是实现了Comparable接口)


Map集合遍历

  1. 通过获取所有的key,通过遍历key,来遍历value
  1. Map<Integer,String>map = new HashMap<>();
  2. map.put(1,"张三");
  3. map.put(2,"李四");
  4. map.put(3,"王五");
  5. Set set = map.keySet(); // 得到所有的key元素
  6. Iterator it = set.iterator(); //拿到迭代器
  7. while(it.hasNext()) {
  8. Integer i = (Integer) it.next(); // 得到迭代的key
  9. String i1 = map.get(i); //通过得到迭代的key 得到value
  10. System.out.println(i);
  11. System.out.println(i1);
  12. }
  1. 增强for
  1. Map<Integer,String>map = new HashMap<>();
  2. map.put(1,"张三");
  3. map.put(2,"李四");
  4. map.put(3,"王五");
  5. Set<Integer> set = map.keySet(); // 得到所有的key元素
  6. for(Integer i : set){
  7. System.out.println(i+"="+map.get(i));
  8. }
  1. 大数据量时使用
  1. Set<Map.Entry<Integer,String>> set1 = map.entrySet();
  2. for (Map.Entry<Integer,String> node :set1){
  3. System.out.println(node.getKey()+"="+node.getValue());
  4. }

Map总结

  1. ArrayList 底层是数组

  2. LinkedList 底层双向链表

  3. Vector 底层是数组,线程安全 效率底,使用较少

  4. HashSet 底层是HashMap,放在HashSet集合中的元素,相当于放在HashMap的key部分

  5. TreeSet 底层是TreeMap,放到TreeSet集合里的元素等同于放在TreeMap集合中的key部分

  6. HashMap 底层是哈希表

  7. Hashtable 底层是哈希表,线程安全,效率低,使用少

  8. Properties 线程安全 并且key和value中只能存储字符串 String

  9. TreeMap 底层是二叉树,TreeMap集合中的key可以自动按照从小到大的顺序排序

集合和IO - 图7


IO

概念

  • I: input O: output

  • java.io.* 包下

  • 通过IO可以完成对硬盘的读和写

字节流

  • 万能流,一次读一个字节(byte) = 8个二进制位
  • 啥都能读取

字符流

  • 一次读一个字符
  • 只能读纯文本文件 .txt

区分

  • 在Java中类名以Stream结尾的都是字节流
  • 以Writer/Reader结尾的都是字符流

所有的流都实现了Closeable接口,都是可以关闭的

  • 流用完一定要关

所有的输出流都实现了Flushable接口,都是可刷新的

  • 最后输出之后一定要flush()刷新一下(清空管道)

集合和IO - 图8

IO四大家族(都是抽象类)

  • java.io.InputStream 字节输入流

  • java.io.OutputStream 字节输出流

  • java.io.Reader 字符输入流

  • java.io.Writer 字符输出流

文件复制原理

集合和IO - 图9

需要掌握的流

文件专属流

  • java.io.FileInputStream

  • java.io.FileOutputStream

  • java.io.FileReader

  • java.io.FileWriter

转换流

(将字节流转换成字符流)

  • java.io.InputStreamReader
  • java.io.OutputStreamWriter

缓冲流专属

  • java.io.BufferedReader

  • java.io.BufferedWriter

  • java.io.BufferedInputStream

  • java.io.BufferedOutputStream

数据流专属

  • java.io.DataInputStream
  • java.io.DataOutputStream

标准输出流

  • java.io.PrintWriter
  • java.io.PrintStream

对象专属流

  • java.io.ObjectInputStream
  • java.io.ObjectOutputStream

File In/Out putStream

万能流,所有文件都可以用来读

int read() 读取第一个字节(每调用一次向下移一个)

int i = stream.read();

返回一个ascll码 读到末尾 ,读不到了就返回-1

int available() 返回流当中剩余的没有读到的字节数量

作用:一上来就可以获取它的总字节数量

int i = stream.available();

long skip() 跳过几个字节不读

用while循环读取文件

  1. FileInputStream s = null;
  2. try {
  3. s = new FileInputStream("I_O/src/FileInputStreamTast01/My");
  4. byte[] bytes = new byte[14]; int i1 = 0 ;
  5. while( (i1 = s.read(bytes)) != -1){
  6. System.out.println(i1);
  7. }
  8. } catch (FileNotFoundException e) {
  9. e.printStackTrace();
  10. } catch (IOException e) {
  11. e.printStackTrace();
  12. } finally{
  13. if (s != null) {
  14. try {
  15. s.close();
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. }

FileOutputStream

写完一定要flush() 刷新一下

构造方法里跟一个地址(只有地址)

会把地址里面的文件清空,后写入

构造方法里跟一个 地址,true 表示追加写入

write() 写入

write(byte[]数组,开始下标,结束下标) 写入指定长度的文件

文件复制

  1. FileInputStream in = null;
  2. FileOutputStream out = null;
  3. try {
  4. in = new FileInputStream("E:\\仝子瑜\\tongziyu.jpg"); //创建输入流
  5. out = new FileOutputStream("E:\\tongziyu.jpg"); //创建输出流
  6. byte[] bytes = new byte[1024];
  7. int i = 0;
  8. while ((i = in.read(bytes)) != -1){ //读文件
  9. out.write(bytes,0,i); //写文件
  10. }
  11. out.flush(); //刷新一下
  12. } catch (FileNotFoundException e) {
  13. e.printStackTrace();
  14. } catch (IOException e) {
  15. e.printStackTrace();
  16. } finally{
  17. if (in != null) {
  18. try {
  19. in.close();
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. if (out != null) {
  25. try {
  26. out.close();
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. }
  30. }

FileReader

一次读取一个字符,只能读取普通文本文件

char数组接收

和FileInputStream差不多,就是用char[]数组接收数据

FileWriter

一个写入一个字符 “a/中”

引用.flush() 刷新不要忘

BufferedReader/PrintStream

BufferedReader概念

  1. 带有缓冲区的字符输入流

  2. 使用时不惜要byte[] 或者 char[] 数组 自带缓冲

  3. 当一个流(包装流,处理流)的构造方法需要传进去一个流(节点流)的时候

  4. 关闭流时只需要管理包装流即可

String readLine() // 读取一行

String s = 引用.readLine();

读不到内容时会返回null

bufferedReader的构造方法里只能跟字符流

如果要用字节流,就需要转换流

转换流
  1. FileInputStream in = new FileInputStream("路径"); //字节流
  2. InputStreamReady ready = new InputStreamReady(in); // 转换成字符流

使用缓冲字符输入流读文件

  1. BufferedReader s = null;
  2. FileReader reader = null;
  3. try {
  4. reader = new FileReader("E:\\java\\Java 资料\\JAVA语法.TXT");
  5. s = new BufferedReader(reader);
  6. String s5 = null;
  7. // 因为 readLine() 在都不到的情况下会返回null 由此判断
  8. while ((s5 = s.readLine()) != null ){
  9. System.out.println(s5);
  10. }
  11. }

PrintStream

  • 标准输出字节

  • 不需要使用close()关闭

  • 标准输出流是指不再向控制台输出,而是指向这个文件输出↓

  • PrintStream ps = new PrintStream(new FileOutputStream(“log”))

  • System.setOut(ps); // 改变输出方向

  • System.out.println(“HelloWorld”); // 不会输出到控制台,会输出到log文件里

File类

和四大家族无关,所以不能读和写

file对象
  • 可能对应的是一个目录,或者文件
  • 只是一个路径名的抽象表示形式

掌握File类的常用方法

  1. File s = new File(“路径”) // 创建file对象

  2. boolean exists(); //判断是否存在

  3. boolean mkdir() //以目录形式新建

  4. boolean mddirs() //以多重目录新建

  5. File getAbsoluteFile() //获取绝对路径

  6. String getParent() //获取父路径

  7. String getName() //获取文件名

序列化

英文集合和IO - 图10
  • 序列化:Serialize
  • 反序列化:DeSerialize

参与序列化的和反序列化的对象必须都要实现Serializable接口

Serializable只是一个标志性接口,里面没有任何代码

在java中如何区分一个类
  • 类名

  • 序列化版本号(实现Serializeble接口)
    Jvm会自动给这个类一个序列化版本号

但是后期修改代码的时候,序列化版本号也会自动修改

所以建议给一个不变的序列化版本号:

private static final long serialVersionVID = ~~~L; //可参考源码

序列化多个对象,创建一个list集合,对象放进去,序列化集合即可

transient关键字(不参加序列化操作)

  • 例如private transient String name;
  • 表示序列化多个对象,创建一个e不参加序列化操作

writeObject(对象或者引用); // 序列化

readObject (); 反序列化

  1. ObjectOutputStream s = null;
  2. ObjectInputStream a = null;
  3. try {
  4. /* s = new ObjectOutputStream(new FileOutputStream("E:\\asd.TXT"));
  5. s.writeObject(1);*/ // 序列化对象
  6. a = new ObjectInputStream(new FileInputStream("E:\\asd.TXT"));
  7. Object s1 = a.readObject(); //反序列化
  8. System.out.println(s1);
  9. } catch (IOException e) {
  10. e.printStackTrace();
  11. } catch (ClassNotFoundException e) {
  12. e.printStackTrace();
  13. } finally{
  14. /*try {
  15. *//*s.close();*//*
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }*/
  19. }

集合的理解和好处

之前保存数据都是用的数组,那么数组有不足的地方,分析如下:

数组缺点:

  • 长度开始时必须指定,而且一旦指定,不能更改

  • 保存的必须为同一类型的元素

  • 使用数组进行增加元素的示意代码 - 比较麻烦

  1. // 写出Person数组扩容示意代码
  2. Person[] pers = new Person[1]; // 大小是1
  3. per[0] = new Person();
  4. // 此时数组已满,增加新的person对象:
  5. Person[] pers2 = new Person[pers.length + 1];
  6. for(){} // 循环将pers数组的东西拷进去
  7. pers2[1] = new Person();
  8. // 第二种数组拷贝的方法 使用System.arraycopy()方法
  9. // 源数组
  10. Integer[] integers = new Integer[5];
  11. for (int i = 0; i < integers.length; i++){
  12. integers[i] = i;
  13. }
  14. // 新的目标数组
  15. Integer[] newIntegers = new Integer[10];
  16. // 参数 1.拷贝源, 2.源的起点, 3.目标, 4.目标的起点, 5.要拷贝多长
  17. // 新的数组中,没有被使用的元素为 null
  18. System.arraycopy(integers,0,newIntegers,0,integers.length);

1)集合概念

  1. 可以动态保存任意多个对象,使用比较方便。

  2. 提供了一系列方便的操作对象的方法:add(),remove(),set(),get()

  3. 使用集合添加,删除新元素的实例代码更加间接明了

1.2、Collection继承图

集合和IO - 图11

1.3、Map继承图

集合和IO - 图12

1.4、Collection接口和常用方法

1)Collection接口实现类的特点

  1. collection实现子类可以存放多个元素,每个元素可以是Object

  2. 有些Collection的实现类,可以存放重复的元素,有些不可以

  3. 有些Collection的实现类,有些事有序的 List, 有些不是有序的Set

  4. Collection接口没有直接的实现子类,是通过他的子接口Set和List来实现的。

2)常用方法

  1. add(元素) 添加一个元素

  2. remove(元素) 删除一个元素,返回一个boolean || remove(索引) 返回被删除的元素

  3. contains(元素) 查找该元素是否存在

  4. size() 获取元素个数

  5. isEmpty() 判断是否为空

  6. clear() 清空

  7. addAll(Collection对象) 添加多个元素

  8. containsAll(Collection对象) 查找多个元素

  9. removeAll(Collection对象) 删除多个元素

  1. // 创建一个集合
  2. Collection collection = new ArrayList();
  3. // add() 添加一个元素
  4. collection.add(1);
  5. collection.add(2);
  6. // remove()删除指定元素
  7. collection.remove(1);
  8. // contains() 查找元素是否存在
  9. boolean contains = collection.contains(2);
  10. // size() 获取元素个数
  11. int size = collection.size();
  12. // isEmpty() 判断是否为空
  13. boolean empty = collection.isEmpty();
  14. // clear() 清空集合
  15. collection.clear();
  16. // addAll() 添加多个元素
  17. Collection collection1 = new ArrayList();
  18. collection1.add(11);
  19. collection1.add(22);
  20. collection1.add(33);
  21. collection.addAll(collection1);
  22. // containsAll(Collection对象) 查找多个元素是否都存在
  23. boolean b = collection.containsAll(collection1);
  24. // removeAll(Collection对象) 删除多个元素
  25. collection.removeAll(collection);

注意
  • 集合中存储的都是引用地址,往里面存入1 实则是存入 new Integer(1); 自动装箱

3)Collection接口遍历元素方式1 - itreator

基本介绍

  1. Iterator对象称为迭代器,主要用于遍历Collection集合中的元素

  2. 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,既可以返回一个迭代器。

  3. Itreator 仅仅用来遍历集合,Iterator本身并不存放对象。