集合
概述
- 集合在JDK java.util.* 包下
所有集合类和集合接口都在
集合是一个载体,一个容器,一次可以容纳多个对象
集合里存的是对象的内存地址
集合本身也是一个对象(有内存地址)
集合里可以存集合
不同的集合底层对应不同的数据结构
集合中不能直接存储基本数据类型,他会自动的帮你装箱
在Java中集合分为两大类
- 一类是单个方式存储元素,超级父接口 java.util.Collection
- 一类是以键值对(key,value)的方式存储元素,超级父接口 java.util.Map
Collection
- Collection 是集合里面的超级父接口
- Collection可以存储那些元素
在没有使用泛型的情况下可以存储Object所有子类
使用了泛型,只能存储某个具体的类型
Collection 常用方法
boolean add(Object e) //向集合里面添加元素(末尾)
int size() //查询集合中元素的个数
void clear() //清空集合
boolean contains(Object o) // 判断集合中是否包含
底层调用了equals()判断,所以放在集合中的元素需要重写equals()方法
如果u2 和集合中的u1 内容完全相同,判断结果是true
- remove(Object o) //删除集合中某个元素
底层调用了equals()判断,所以放在集合中的元素需要重写equals()方法
如果u2 和集合中的u1 内容完全相同,删除u2,u1会被删除
boolean isEmply() //判断集合是否为空 空=true 不空 =false
Object[] toArray() //将集合转化成数组,返回一个Object数组
Iterator iterator() //拿到迭代器 返回一个Iterator
增强for循环
用来遍历集合/遍历数组
for(元素类型 变量名 : 数组或者集合){
循环体 //System.out.println(变量名);遍历集合
}
collection 继承结构图
List/Set/SortedSet特点
集合不能直接存储基本数据类型,另外集合也不能直接存储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集合常用方法
void add(下标,元素) //在指定的位置插入元素
Object get(下标) //得到指定位置的元素
int indexOf("元素") //得到该元素第一次出现的下标
int lastIndexOf("元素") //得到该元素最后一次出现的下标
Object remove(下标) // 删除指定位置的元素
Object set(下标,元素) //修改指定下标的元素
链表
优点
随机增删元素效率较高
缺点
查询效率底
在开发中如果遇到了随机增删元素较多的业务时,建议使用LinkedList
ArrayList
ArrayList集合初始化容量10
扩容为原容量1.5倍。
底层是数组。
数组优点和缺点要能够说出来!
另外要注意:ArrayList集合末尾增删元素效率还是可以的。
Vector:
Vector初始化容量是10.
扩容为原容量的2倍。
底层是数组。
Vector底层是线程安全的。
怎么得到一个线程安全的List:
Collections.synchronizedList(list);
集合的遍历/迭代器
此内容只能在Collection中使用Map集合不能使用
Iterator i = 集合.iterator(); // 拿到迭代器
迭代器常用方法
boolean hasNext()
如果返回true表示有元素可以迭代
如果返回false表示没有元素可以迭代
Object next();
让迭代器前进一位,并且拿到该元素
迭代器对象最初没有指向任何元素
遍历集合
Collection c = new ArrayList(); // 创建集合 多态
Iterator i = c.iterator(); //拿到迭代器
while(i.hasNext()){ //如果有元素返回true ,执行循环,没有则退出
System.out.println(i.next()); // 迭代
}
注意
- 当集合发生结构变化,一定要重新获取迭代器
- 在迭代元素时不能调用c.remove()方法 ,删除元素但是可以调用i.remove()方法 ,删除元素
泛型
在JDK5.0出现的新特性
List myList = new ArrayList<>(); <>钻石表达式
使用泛型后myList集合中只允许出现Animal类或者他的子类
优点
使用泛型后,集合中的数据类型更加统一
缺点
导致集合中的储存元素缺乏多样性
注意
使用泛型之后迭代器迭代的就不是Object类型了而是<> 这里面的类型
Collection-TreeSet集合/Vector集合
底层是TreeMap
TreeMap底层是二叉树
放到TreeSet集合中的元素 等同于放到TreeMap的key部分
特点
无序不可重复
自动排序,从小到大
排序可以自定义
比较规则
升序 : return this.age-c.age;
降序 : return c.age - this.age;
例如
TreeSet<B> treeSet2 = new TreeSet<>(new Comparator<B>() {
@Override
public int compare(B b, B t1) {
return b.age - t1.age;
} //匿名内部类
});
treeSet2.add(new B(520));
treeSet2.add(new B(500));
treeSet2.add(new B(510));
treeSet2.add(new B(400));
treeSet2.add(new B(300));
for ( B b : treeSet2){
System.out.println(b);
}
结论
- 放到TreeSet或者TreeMap集合中key部分想要排序包括两种方式
- 集合中的元素类中实现Comparable接口
- 在构造的时候传一个比较器Comparator接口(对象)
- 如果比较规则不变使用Comparable接口
- 如果比较规则有多个,需要切换,用Comparator接口(匿名内部类)
Vector 集合
底层是数组
初始容量为10
扩容之后是原容量的2倍 10—>20—>40
所有的方法都是线程同步的(所以不常用)
Collection-集合继承结构图
Map
概念
Map集合以key和value的方式储存数据(键值对)
key和value是引用数据类型
key和value保存的是对象的内存地址
key起主导作用,value是附属品
Map集合常用方法
put(k,v) //向Map集合中添加键值对
get(Object key) // 通过key找到value
void clear() //清空集合
boolean containsKey(Object key) // 判断集合里有没有key(只在key中找)
boolean contiansValue(Object value) //判断集合中有没有value(只在value中找)
boolean isEmpty() //判断集合是否为空
int size() //获取键值对的数量,一组为一个
remove (Object key) // 通过key来删除键值对
Collection values() // 得到所有的value元素 用一个Collection 集合接收
Set keySet() //得到所有的key元素 用Set集合接收
哈希表数据结构(散列表)
哈希表是数组和单向链表的结合体
哈希表就是将传进来的key元素,通过hashCode算出来一个下标,将key存进去
如果重写hashCode方法返回都是一样的值,那么就变成了纯链表
如果重写的hashCode方法返回的值都不一样,那么就变成了纯数组
如果put(k重复了,value会覆盖)
重点
放在HashMap/HashSet集合里key部分的元素必须同时重写HashCode()和equals()
hashCode()和equals()方法不需要自己写直接idea生成
HashMap集合初始化容量必须是2的次幂,这也是官方推荐 【推荐16】
简易Map继承图
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集合遍历
- 通过获取所有的key,通过遍历key,来遍历value
Map<Integer,String>map = new HashMap<>();
map.put(1,"张三");
map.put(2,"李四");
map.put(3,"王五");
Set set = map.keySet(); // 得到所有的key元素
Iterator it = set.iterator(); //拿到迭代器
while(it.hasNext()) {
Integer i = (Integer) it.next(); // 得到迭代的key
String i1 = map.get(i); //通过得到迭代的key 得到value
System.out.println(i);
System.out.println(i1);
}
- 增强for
Map<Integer,String>map = new HashMap<>();
map.put(1,"张三");
map.put(2,"李四");
map.put(3,"王五");
Set<Integer> set = map.keySet(); // 得到所有的key元素
for(Integer i : set){
System.out.println(i+"="+map.get(i));
}
- 大数据量时使用
Set<Map.Entry<Integer,String>> set1 = map.entrySet();
for (Map.Entry<Integer,String> node :set1){
System.out.println(node.getKey()+"="+node.getValue());
}
Map总结
ArrayList 底层是数组
LinkedList 底层双向链表
Vector 底层是数组,线程安全 效率底,使用较少
HashSet 底层是HashMap,放在HashSet集合中的元素,相当于放在HashMap的key部分
TreeSet 底层是TreeMap,放到TreeSet集合里的元素等同于放在TreeMap集合中的key部分
HashMap 底层是哈希表
Hashtable 底层是哈希表,线程安全,效率低,使用少
Properties 线程安全 并且key和value中只能存储字符串 String
TreeMap 底层是二叉树,TreeMap集合中的key可以自动按照从小到大的顺序排序
IO
概念
I: input O: output
java.io.* 包下
通过IO可以完成对硬盘的读和写
字节流
- 万能流,一次读一个字节(byte) = 8个二进制位
- 啥都能读取
字符流
- 一次读一个字符
- 只能读纯文本文件 .txt
区分
- 在Java中类名以Stream结尾的都是字节流
- 以Writer/Reader结尾的都是字符流
所有的流都实现了Closeable接口,都是可以关闭的
- 流用完一定要关
所有的输出流都实现了Flushable接口,都是可刷新的
- 最后输出之后一定要flush()刷新一下(清空管道)
IO四大家族(都是抽象类)
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
文件复制原理
需要掌握的流
文件专属流
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循环读取文件
FileInputStream s = null;
try {
s = new FileInputStream("I_O/src/FileInputStreamTast01/My");
byte[] bytes = new byte[14]; int i1 = 0 ;
while( (i1 = s.read(bytes)) != -1){
System.out.println(i1);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if (s != null) {
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
FileOutputStream
写完一定要flush() 刷新一下
构造方法里跟一个地址(只有地址)
会把地址里面的文件清空,后写入
构造方法里跟一个 地址,true 表示追加写入
write() 写入
write(byte[]数组,开始下标,结束下标) 写入指定长度的文件
文件复制
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("E:\\仝子瑜\\tongziyu.jpg"); //创建输入流
out = new FileOutputStream("E:\\tongziyu.jpg"); //创建输出流
byte[] bytes = new byte[1024];
int i = 0;
while ((i = in.read(bytes)) != -1){ //读文件
out.write(bytes,0,i); //写文件
}
out.flush(); //刷新一下
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
FileReader
一次读取一个字符,只能读取普通文本文件
用char数组接收
和FileInputStream差不多,就是用char[]数组接收数据
FileWriter
一个写入一个字符 “a/中”
引用.flush() 刷新不要忘
BufferedReader/PrintStream
BufferedReader概念
带有缓冲区的字符输入流
使用时不惜要byte[] 或者 char[] 数组 自带缓冲
当一个流(包装流,处理流)的构造方法需要传进去一个流(节点流)的时候
关闭流时只需要管理包装流即可
String readLine() // 读取一行
String s = 引用.readLine();
读不到内容时会返回null
bufferedReader的构造方法里只能跟字符流
如果要用字节流,就需要转换流
转换流
FileInputStream in = new FileInputStream("路径"); //字节流
InputStreamReady ready = new InputStreamReady(in); // 转换成字符流
使用缓冲字符输入流读文件
BufferedReader s = null;
FileReader reader = null;
try {
reader = new FileReader("E:\\java\\Java 资料\\JAVA语法.TXT");
s = new BufferedReader(reader);
String s5 = null;
// 因为 readLine() 在都不到的情况下会返回null 由此判断
while ((s5 = s.readLine()) != null ){
System.out.println(s5);
}
}
PrintStream
标准输出字节
不需要使用close()关闭
标准输出流是指不再向控制台输出,而是指向这个文件输出↓
PrintStream ps = new PrintStream(new FileOutputStream(“log”))
System.setOut(ps); // 改变输出方向
System.out.println(“HelloWorld”); // 不会输出到控制台,会输出到log文件里
File类
和四大家族无关,所以不能读和写
file对象
- 可能对应的是一个目录,或者文件
- 只是一个路径名的抽象表示形式
掌握File类的常用方法
File s = new File(“路径”) // 创建file对象
boolean exists(); //判断是否存在
boolean mkdir() //以目录形式新建
boolean mddirs() //以多重目录新建
File getAbsoluteFile() //获取绝对路径
String getParent() //获取父路径
String getName() //获取文件名
序列化
英文
- 序列化:Serialize
- 反序列化:DeSerialize
参与序列化的和反序列化的对象必须都要实现Serializable接口
Serializable只是一个标志性接口,里面没有任何代码
在java中如何区分一个类
类名
序列化版本号(实现Serializeble接口)
Jvm会自动给这个类一个序列化版本号
但是后期修改代码的时候,序列化版本号也会自动修改
所以建议给一个不变的序列化版本号:
private static final long serialVersionVID = ~~~L; //可参考源码
序列化多个对象,创建一个list集合,对象放进去,序列化集合即可
transient关键字(不参加序列化操作)
- 例如private transient String name;
- 表示序列化多个对象,创建一个e不参加序列化操作
writeObject(对象或者引用); // 序列化
readObject (); 反序列化
ObjectOutputStream s = null;
ObjectInputStream a = null;
try {
/* s = new ObjectOutputStream(new FileOutputStream("E:\\asd.TXT"));
s.writeObject(1);*/ // 序列化对象
a = new ObjectInputStream(new FileInputStream("E:\\asd.TXT"));
Object s1 = a.readObject(); //反序列化
System.out.println(s1);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally{
/*try {
*//*s.close();*//*
} catch (IOException e) {
e.printStackTrace();
}*/
}
集合的理解和好处
之前保存数据都是用的数组,那么数组有不足的地方,分析如下:
数组缺点:
长度开始时必须指定,而且一旦指定,不能更改
保存的必须为同一类型的元素
使用数组进行增加元素的示意代码 - 比较麻烦
// 写出Person数组扩容示意代码
Person[] pers = new Person[1]; // 大小是1
per[0] = new Person();
// 此时数组已满,增加新的person对象:
Person[] pers2 = new Person[pers.length + 1];
for(){} // 循环将pers数组的东西拷进去
pers2[1] = new Person();
// 第二种数组拷贝的方法 使用System.arraycopy()方法
// 源数组
Integer[] integers = new Integer[5];
for (int i = 0; i < integers.length; i++){
integers[i] = i;
}
// 新的目标数组
Integer[] newIntegers = new Integer[10];
// 参数 1.拷贝源, 2.源的起点, 3.目标, 4.目标的起点, 5.要拷贝多长
// 新的数组中,没有被使用的元素为 null
System.arraycopy(integers,0,newIntegers,0,integers.length);
1)集合概念
可以动态保存任意多个对象,使用比较方便。
提供了一系列方便的操作对象的方法:add(),remove(),set(),get()
使用集合添加,删除新元素的实例代码更加间接明了
1.2、Collection继承图
1.3、Map继承图
1.4、Collection接口和常用方法
1)Collection接口实现类的特点
collection实现子类可以存放多个元素,每个元素可以是Object
有些Collection的实现类,可以存放重复的元素,有些不可以
有些Collection的实现类,有些事有序的 List, 有些不是有序的Set
Collection接口没有直接的实现子类,是通过他的子接口Set和List来实现的。
2)常用方法
add(元素) 添加一个元素
remove(元素) 删除一个元素,返回一个boolean || remove(索引) 返回被删除的元素
contains(元素) 查找该元素是否存在
size() 获取元素个数
isEmpty() 判断是否为空
clear() 清空
addAll(Collection对象) 添加多个元素
containsAll(Collection对象) 查找多个元素
removeAll(Collection对象) 删除多个元素
// 创建一个集合
Collection collection = new ArrayList();
// add() 添加一个元素
collection.add(1);
collection.add(2);
// remove()删除指定元素
collection.remove(1);
// contains() 查找元素是否存在
boolean contains = collection.contains(2);
// size() 获取元素个数
int size = collection.size();
// isEmpty() 判断是否为空
boolean empty = collection.isEmpty();
// clear() 清空集合
collection.clear();
// addAll() 添加多个元素
Collection collection1 = new ArrayList();
collection1.add(11);
collection1.add(22);
collection1.add(33);
collection.addAll(collection1);
// containsAll(Collection对象) 查找多个元素是否都存在
boolean b = collection.containsAll(collection1);
// removeAll(Collection对象) 删除多个元素
collection.removeAll(collection);
注意
- 集合中存储的都是引用地址,往里面存入1 实则是存入 new Integer(1); 自动装箱
3)Collection接口遍历元素方式1 - itreator
基本介绍
Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,既可以返回一个迭代器。
Itreator 仅仅用来遍历集合,Iterator本身并不存放对象。