API+IO+泛型+集合基础
大家好, 我是鹿是不是鹿, 以下是关于API+IO+泛型+集合相关的一些基础知识, 希望大家喜欢。
1.API
API:Application Programming Interface应用编程接口,一切可以调用的东西都是API。
java.lang包,这个包会自动导入。
java.lang.Object
java.lang.String
java.lang.StringBuilder/StringBuffer
正则表达式
包装类等
1.1 Object
1.1.1 概念
所有对象的顶级父类
存在于java.lang包中,这个包不需要我们手动导包

1.1.2 常用方法
boolean equals(Object obj)
指示其他某个对象是否与此对象“相等”。
protected void finalize()
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
int hashCode()
返回该对象的哈希码值。
String toString()
返回该对象的字符串表示。
1.1.3 toString()
默认返回 类名@地址 的格式,来展示对象的地址值,如:a00000.Student@a0834e7。
如果想看属性值我们可以重写这个方法,重写后返回的就是把属性值拼接成一个字符串。
如:Student [name=苏大强, age=20, id=10001]
1.1.4 equals(Object obj)
1.1.5 hashCode()
返回该对象的哈希码值。
1.2 String
字符串对象
1.2.1 特点
是一个封装char[]数组的对象

字符串不可变

1.2.2 创建String对象

1、 如果是第一次使用字符串,java会在字符串常量池创建一个对象。
2、 再次使用相同的内容时,会直接访问常量池中存在的对象。
方式1:new String(char[])
其实字符串底层维护了一个char[]
char[] c = {'a','b','c','d'};
String s = new String(c);//堆中分配新的内存
System.out.println(s);
方式2:直接创建
常量池里直接创建对象(本质还是char[]),再次使用相同内容,会去常量池中找到已经存在的对象,不会新建。
String s2="abcd";//常量池中分配新的内存
System.out.println(s2);
System.out.println(s==s2);//地址不同
System.out.println(s.equals(s2));//内容相同
//如果使用过了就不再创建,引用存在的对象
String s3="abcd";//访问常量池中已经存在的对象
System.out.println(s3==s2);//true
1.2.3 字符串连接效率
利用String类,在做字符串拼接的过程效率极其低下。
1.2.4 常用方法
length()
charAt()
lastIndexOf()
substring()
equals()
startsWith()
endsWith()
split()
trim() 去除字符串两端的空格
1.2.5 测试
1.3 StringBuilder/StringBuffer
1.3.1 特点
1、 封装了char[]数组
2、 是可变的字符序列
3、 提供了一组可以对字符内容修改的方法
4、 常用append()来代替字符串做字符串连接
5、 内部字符数组默认初始容量是16:initial capacity of 16 characters
6、 如果大于16会尝试将扩容,新数组大小原来的变成2倍+2,容量如果还不够,直接扩充到需要的容量大小。int newCapacity = value.length * 2 + 2;
7、 StringBuffer 1.0出道线程安全,StringBuilder1.5出道线程不安全
1.3.2 练习:测试字符串连接
1.3.3 方法
append()
charAt()
1.4 包装类
1.4.1 与基本类型的对应关系

1.4.2 Number
数字包装类的抽象父类。
子类:

常用的方法:
提供了各种获取值的方式,已经完成了强转。

1.4.3 Integer
创建对象
new Integer(5);新建对象
Integer.valueOf(5);
在Integer类中,包含256个Integer缓存对象,范围是 -128到127。
使用valueOf()时,如果指定范围内的值,访问缓存对象,而不新建;如果指定范围外的值,直接新建对象。
Integer a = new Integer(5);//创建对象
Integer b = Integer.valueOf(5);//读取缓存
Integer c = Integer.valueOf(5);//读取缓存
System.out.println(b==c);//true
System.out.println(a==b);//false
System.out.println(a.equals(b));//true
方法
parseInt();字符串转换成int
toBinaryString();把整数转换成2进制数据
toOctalString();把整数转换成8进制数据
toHexString();把整数转换成16进制数据
1.4.4 Double
创建对象
new Double(3.14)
Double.valueOf(3.14)//和 new 没有区别
方法
Double.parseDouble();
1.5 日期类Date
1.5.1 概述
存在于java.util.Date包。
用来封装一个毫秒值表示一个精确的时间点。
从1970-1-1 0点开始的毫秒值。
1.5.2 创建对象
new Date():封装的是系统当前时间的毫秒值
new Date(900000000000L):封装指定的时间点
1.5.3 常用方法
getTime():取内部毫秒值
setTime():存取内部毫秒值
getMonth():获取当前月份
getHours():获取当前小时
compareTo(Date):当前对象与参数对象比较。当前对象大返回正数,小返回负数,相同0。
1.5.4 练习1:测试日期类的常用方法
1.6 日期工具SimpleDateFormat
1.6.1 概述
日期格式化工具,可以把Date对象格式化成字符串,也可以日期字符串解析成Date对象。
1.6.2 创建对象
new SimpleDateFormat(格式)
格式:yyyy-MM-dd HH:mm:ss
MM/dd/yyyy..
1.6.3 常见方法
format(Date):把Date格式化成字符串
parse(String):把String解析成Date
1.6.4 练习1 :计算存活天数
接收用户输入的出生日期,计算存活天数
1.7 拓展
1.7.1 进制
概念
进制也就是进位计数制,是人为定义的带进位的计数方法,类似于统计“正”字。
对于任何一种进制---X进制,就表示每一位置上的数运算时都是逢X进一位。
十进制是逢十进一,十六进制是逢十六进一,二进制就是逢二进一,以此类推。
通常情况下,1byte=8个二进制位
所以表示一个数字用二进制来表示的话就可以这样表示:0000 0000
把这8个位进行组合,每三位组合就形成了八进制,每四位组合就形成了十六进制。
特点
二进制:0和1,逢二进一,以0b开始
八进制:0-7,逢八进一,以0开始
十进制:0-9,逢十进一
16进制:0-9,abcdef,逢16进一,以0x开始
进制的转化:
十进制转二进制:不断除以2商0为止,取余,倒着写。
把十进制11转成2进制:1011。

二进制转十进制:从低位次,每位乘以2的位次次幂 再求和。
计算二进制数据:0000 1101对应的十进制
计算二进制数据:0110 1110对应的十进制

二进制转八进制:从低位次开始,每三位为一组,产生一个八进制数字,最高位不足补零。
计算二进制数据110 0111对应的八进制

八进制转二进制:把一个数字转为3个数字,不足三位的,最高位补零。
计算八进制数据:023 0653对应的二进制数据

1.7.2 StringBuilder和StringBuffer的区别
1、 在线程安全上,
--StringBuffer是旧版本就提供的,线程安全的。@since JDK1.0
--StringBuilder是jdk1.5后产生,线程不安全的。@since 1.5
2、 在执行效率上,StringBuilder > StringBuffer > String
3、 源码体现:本质上都是在调用父类抽象类AbstractStringBuilder来干活,只不过Buffer把代码加了同步关键字,使得程序可以保证线程安全问题。
abstract class AbstractStringBuilder implements Appendable, CharSequence {


1.7.3 自动装箱和自动拆箱
自动装箱:把基本类型包装成一包装类的对象
Integer a = 5;//a是引用类型,引用了包装对象的地址。
编译器会完成对象的自动装箱:Integer a = Integer.valueOf(5);
自动拆箱:从包装对象中,自动取出基本类型值
int i = a;//a现在是包装类型,没法给变量赋值,需要把5取出来。
编译器会完成自动拆箱:int i = a.intValue();
2.1 BigDecimal/BigInteger
2.1.1 概述
BigDecimal:常用来解决精确的浮点数运算。
BigInteger:常用来解决超大的整数运算。
2.1.2 创建对象
BigDecimal.valueOf(2);
2.1.3 常用方法
add(BigDecimal bd): 做加法运算
substract(BigDecimal bd) : 做减法运算
multiply(BigDecimal bd) : 做乘法运算
divide(BigDecimal bd) : 做除法运算
divide(BigDecimal bd,保留位数,舍入方式):除不尽时使用
setScale(保留位数,舍入方式):同上
pow(int n):求数据的几次幂
2.1.4 练习1:测试常用方法
接收用户输入的两个数字,做运算。
2.2 IO简介
2.2.1 继承结构
in/out相对于程序而言的输入(读取)和输出(写出)的过程。
在Java中,根据处理的数据单位不同,分为字节流和字符流
java.io包:
File
字节流:针对二进制文件
InputStream
--FileInputStream
--BufferedInputStream
--ObjectInputStream
OutputStream
--FileOutputStream
--BufferedOutputStream
--ObjectOutputStream
字符流:针对文本文件。读写容易发生乱码现象,在读写时最好指定编码集为utf-8
Writer
--BufferedWriter
--OutputStreamWriter
Reader
--BufferedReader
--InputStreamReader
--PrintWriter/PrintStream
2.2.2 流的概念
数据的读写抽象成数据,在管道中流动。
Ø 流只能单方向流动
Ø 输入流用来读取in
Ø 输出流用来写出Out
Ø 数据只能从头到尾顺序的读写一次

2.3 File文件流
2.3.1 概述
封装一个磁盘路径字符串,对这个路径可以执行一次操作。
可以用来封装文件路径、文件夹路径、不存在的路径。
2.3.2 创建对象
File(String pathname)
通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
2.3.3 常用方法
文件、文件夹属性
length():文件的字节量
exists():是否存在,存在返回true
isFile():是否为文件,是文件返回true
isDirectory():是否为文件夹,是文件夹返回true
getName():获取文件/文件夹名
getParent():获取父文件夹的路径
getAbsolutePath():获取文件的完整路径
创建、删除
createNewFile():新建文件,文件夹不存在会异常,文件已经存在返回false
mkdirs():新建多层不存在的文件夹\a\b\c
mkdir():新建单层不存在的文件夹\a
delete():删除文件,删除空文件夹
文件夹列表
list():返回String[],包含文件名
listFiles():返回File[],包含文件对象
2.3.4 练习1:测试常用方法
创建day10工程
创建cn.tedu.io包
创建Test1.java
2.3.5 练习2:递归求目录总大小
递归:不断的调用方法本身。
2.4 字节流读取
字节流是由字节组成的,字符流是由字符组成的. Java里字符由两个字节组成.字节流是最基本的,所有的InputStream和OutputStream的子类都是,主要用在处理二进制数据。
流式传输主要指将整个音频和视频及三维媒体等多媒体文件经过特定的压缩方式解析成一个个压缩包,由视频服务器向用户计算机顺序或实时传送。在采用流式传输方式的系统中,用户不必像采用下载方式那样等到整个文件全部下载完毕,而是只需经过几秒或几十秒的启动延时即可在用户的计算机上利用解压设备对压缩的A/V、3D等多媒体文件解压后进行播放和观看。此时多媒体文件的剩余部分将在后台的服务器内继续下载。
2.4.1 InputStream抽象类
此抽象类是表示字节输入流的所有类的超类/抽象类。
常用方法:
abstract int read()
从输入流中读取数据的下一个字节。
int read(byte[] b)
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
int read(byte[] b, int off, int len)
将输入流中最多 len 个数据字节读入 byte 数组。
void close()
关闭此输入流并释放与该流关联的所有系统资源。
2.4.2 FileInputStream子类
直接插在文件上,直接读取文件数据。
创建对象
FileInputStream(File file)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
FileInputStream(String pathname)
通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
2.4.3 BufferedInputStream子类
BufferedInputStream 为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组(默认8M大小)。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。
创建对象
BufferedInputStream(InputStream in)
创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
2.5 字符流读取
常用于处理纯文本数据。
2.5.1 Reader抽象类
用于读取字符流的抽象类。
常用方法:
int read()
读取单个字符。
int read(char[] cbuf)
将字符读入数组。
abstract int read(char[] cbuf, int off, int len)
将字符读入数组的某一部分。
int read(CharBuffer target)
试图将字符读入指定的字符缓冲区。
abstract void close()
关闭该流并释放与之关联的所有资源。
2.5.2 InputStreamReader子类
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
创建对象
InputStreamReader(InputStream in, String charsetName)
创建使用指定字符集的 InputStreamReader。
InputStreamReader(InputStream in)
创建一个使用默认字符集的 InputStreamReader。
2.5.3 FileReader子类
用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。
创建对象
FileReader(
String fileName)
在给定从中读取数据的文件名的情况下创建一个新 FileReader。
FileReader(
File file)
在给定从中读取数据的 File 的情况下创建一个新 FileReader。
2.5.4 BufferedReader子类
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
创建对象
BufferedReader(Reader in)
创建一个使用默认大小输入缓冲区的缓冲字符输入流。
2.6 练习:文件的读取
读取指定文件
2.7 扩展1
2.7.1 字符流和字节流的区别
2.7.2 字符流读写乱码
new BufferedReader(new InputStreamReader(?,”utf-8”));
new BufferedWriter(new OutputStreamWriter(?,”utf-8”));
//默认是系统的编码,GBK写出。
//如果打开和写出的编码用的表不一致,会造成乱码。
OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream("encode.txt"),"utf-8");
os.write("中国");
os.flush();
2.7.3 常见字符编码表

测试
@Test
public void code() throws IOException {
String s = "我爱你中国";
System.out.println(s.getBytes("utf-8").length);//15--unicode/u8一个汉字3字节存储
System.out.println(s.getBytes("gbk").length);//10--中文双字节
System.out.println(s.getBytes("unicode").length);//12--双字节+2
System.out.println(s.getBytes("iso-8859-1").length);//5--单字节
}
2.7.4 JDK1.7新特性之IO关流
3.1 字节流写出
3.1.1 OutputStream抽象类
此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器。
常用方法:
void close()
关闭此输出流并释放与此流有关的所有系统资源。
void flush()
刷新此输出流并强制写出所有缓冲的输出字节。
void write(byte[] b)
将 b.length 个字节从指定的 byte 数组写入此输出流。
void write(byte[] b, int off, int len)
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
abstract void write(int b)
将指定的字节写入此输出流。
3.1.2 FileOutputStream子类
直接插在文件上,直接写出文件数据
创建对象:
FileOutputStream(String name)
创建一个向具有指定名称的文件中写入数据的输出文件流。FileOutputStream(File file)
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
FileOutputStream(File file, boolean append) –追加
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
3.1.3 BufferedOutputStream子类
该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
创建对象
BufferedOutputStream(OutputStream out)
创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
3.2 字符流写出
3.2.1 Writer抽象类
写入字符流的抽象类。
常用方法:
void write(char[] cbuf)
写入字符数组。
abstract void write(char[] cbuf, int off, int len)
写入字符数组的某一部分。
void write(int c)
写入单个字符。
void write(String str)
写入字符串。
void write(String str, int off, int len)
写入字符串的某一部分。
abstract void close()
关闭此流,但要先刷新它。
3.2.2 OutputStreamWriter子类
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
创建对象
OutputStreamWriter(OutputStream out, String charsetName)
创建使用指定字符集的 OutputStreamWriter。
OutputStreamWriter(OutputStream out)
创建使用默认字符编码的 OutputStreamWriter。
3.2.3 FileWriter子类
用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。
创建对象
FileWriter(
String fileName)
根据给定的文件名构造一个 FileWriter 对象。
FileWriter(
String fileName, boolean append)
根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
3.2.4 BufferedWriter子类
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
创建对象
BufferedWriter(Writer out)
创建一个使用默认大小输出缓冲区的缓冲字符输出流。
3.3 练习:文件的写出
把数据写出到指定文件中。如果文件不存在会自动创建,文件夹不存在会报错。
3.4 IO综合练习
3.4.1 练习1:文件复制
from,to。读取from的数据。写出到to文件里
3.4.2 练习2:批量读写
3.5 序列化 / 反序列化
3.5.1 概述
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。
在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
序列化:利用ObjectOutputStream,对象的信息,按固定格式转成一串字节值输出并持久保存到磁盘化。
反序列化:利用ObjectInputStream,读取磁盘中序列化数据,重新恢复对象。

3.5.2 特点/应用场景
1、 需要序列化的文件必须实现Serializable接口以启用其序列化功能。
2、 不需要序列化的数据可以被修饰为static的,由于static属于类,不随对象被序列化输出。
3、 不需要序列化的数据也可以被修饰为transient临时的,只在程序运行期间,在内存中存在不会被序列化持久保存。
4、 在反序列化时,如果和序列化的版本号不一致时,无法完成反序列化。
5、 每个被序列化的文件都有一个唯一id,如果没有添加编译器会根据类的定义信息计算产生一个版本号。
6、 常用于服务器之间的数据传输,序列化成文件,反序列化读取数据。
7、 常用于使用套接字流在主机之间传递对象。
3.5.3 ObjectOutputStream
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。
ObjectOutputStream(OutputStream out)
创建写入指定 OutputStream 的 ObjectOutputStream。
void writeObject(Object obj)
将指定的对象写入 ObjectOutputStream。
3.5.4 ObjectInputStream
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
ObjectInputStream(InputStream in)
创建从指定 InputStream 读取的 ObjectInputStream。
Object readObject()
从 ObjectInputStream 读取对象,读取序列化数据。
3.5.5 练习1:将学生信息序列化至磁盘【序列化】
3.6 编码转换流
用来作为桥梁,把字节流转成字符流的桥梁。
用来解决字符流读写乱码问题。
3.6.1 工具类
OutputStreamWriter:是字节流通向字符流的桥梁
--OutputStreamWriter(OutputStream out, String charsetName)
--OutputStreamWriter(OutputStream out)
InputStreamReader:是字节流通向字符流的桥梁
--InputStreamReader(InputStream in)
--InputStreamReader(InputStream in, String charsetName)
3.6.2 常见字符编码表

3.6.3 测试
3.7 扩展
3.7.1 IO中flush()和close()的区别
3.7.2 封装释放资源的close()
public static void close(Closeable io) {
if (io != null) {
try {
io.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.7.3 BIO、NIO、AIO的区别
阻塞IO,BIO 就是传统的 java.io 包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的有点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
非阻塞IO,NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。
异步IO,AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。但目前还不够成熟,应用不多。
3.7.4 数组和链表区别
List是一个接口,它有两个常用的子类,ArrayList和LinkedList,看名字就可以看得出一种是基于数组实现另一个是基于链表实现的。
数组ArrayList遍历快,因为存储空间连续;链表LinkedList遍历慢,因为存储空间不连续,要去通过指针定位下一个元素,所以链表遍历慢。
数组插入元素和删除元素需要重新申请内存,然后将拼接结果保存进去,成本很高。例如有100个值,中间插入一个元素,需要数组重新拷贝。而这个动作对链表来说,太轻松了,改变一下相邻两个元素的指针即可。所以链表的插入和修改元素时性能非常高。
实际开发就根据它们各自不同的特点来匹配对应业务的特点。业务一次赋值,不会改变,顺序遍历,就采用数组;业务频繁变化,有新增,有删除,则链表更加适合。
3.7.5 读一行写一行
4.1 泛型
4.1.1 概念
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}
public interface Deque<E> extends Queue<E> {}
public interface Queue<E> extends Collection<E> {}
public interface Collection<E> extends Iterable<E> {}
我们上面的代码中出现的<?>是什么东西呢 它叫泛型,常用来和集合对象一同使用,所以我们在开始学习集合之前,必须先了解下什么是泛型。而且泛型概念非常重要,它是程序的增强器,它是目前主流的开发方式。
泛型是(Generics)是JDK1.5 的一个新特性,其实就是一个『语法糖』,本质上就是编译器为了提供更好的可读性而提供的一种小手段,小技巧,虚拟机层面是不存在所谓『泛型』的概念的。
4.1.2 作用
l 通过泛型的语法定义,约束集合元素的类型,进行安全检查,把错误显示在编译期
l 代码通用性更强,后面有案例
l 泛型可以提升程序代码的可读性,但它只是一个语法糖(编译后这样的东西就被删除,不出现在最终的源代码中),对于JVM运行时的性能是没有任何影响的。
4.1.3 泛型示例

我们创建一个ArrayList,上面看到eclipse提示有个黄线,什么意思呢?
ArrayList is a raw type. References to generic type ArrayList<E> should be parameterized.
ArrayList使用了泛型,在声明时需指定具体的类型<E>。
那我们把这个<>里的方式就称为泛型。上面的泛型有什么作用呢?就是在编译阶段就检查我们传入的参数类型是否正确。

有了泛型,我们可以看到人家要求存放String,而我故意存放的整数100,所以eclipse提示我们错误:
The method add(int, String) in the type List<String> is not applicable for the arguments (int)。
类型List<String>的add方法要求增加的类型为String类型,不正确不能存入。
4.1.4 泛型声明
泛型可以在接口、方法、返回值上使用:
java.util.List泛型接口/类:
public interface Collection<E> {}
泛型方法的声明:
public <E> void print(E e) {}
在方法返回值前声明了一个<E>表示后面出现的E是泛型,而不是普通的java变量。
4.1.5 常用名称
l E - Element (在集合中使用,因为集合中存放的是元素)
l T - Type(Java 类)
l K - Key(键)
l V - Value(值)
l N - Number(数值类型)
l ? - 表示不确定的java类型
4.1.6 用途:编译时类型检查
4.1.7 用途:代码通用性更强
传统方式通过重载多态实现,方法同名,参数类型不同。
泛型方式
4.1.8 类型擦除
泛型只是在编译期间生存,编译后就被干掉了,真正运行时,大多情况下取而代之的是Object。
下面的代码利用了jdk提供的强大的反射功能,后续会专门详细讲解,今天先初体验下其强大的功能。
4.2 Collection接口
4.2.1 概述
英文名称Collection,是用来存放对象的数据结构。其中长度可变,而且集合中可以存放不同类型的对象。并提供了一组操作成批对象的方法。
数组的缺点:长度是固定不可变的,访问方式单一,插入、删除等操作繁琐。
4.2.2 集合的继承结构

Collection接口
-- List接口 : 数据有序,可以重复。
-- ArrayList子类
-- LinkedList子类
-- Set接口 : 数据无序,不可以存重复值
-- HashSet子类
-- Map接口 : 键值对存数据
-- HashMap
Collections工具类
4.2.3 常用方法
boolean add(E e):添加元素。
boolean addAll(Collection c):把小集合添加到大集合中 。
boolean contains(Object o) : 如果此 collection 包含指定的元素,则返回 true。
boolean isEmpty() :如果此 collection 没有元素,则返回 true。
Iterator<E> iterator():返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。
int size() :返回此 collection 中的元素数。
Objec[] toArray():返回对象数组
4.2.4 练习1:测试常用方法
4.3 List接口
4.3.1 概述
有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
4.3.2 特点
1、 数据有序
2、 允许存放重复元素
3、 元素都有索引
4.3.3 常用方法
ListIterator<E> listIterator()
返回此列表元素的列表迭代器(按适当顺序)。
ListIterator<E> listIterator(int index)
返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。
void add(int index, E element)
在列表的指定位置插入指定元素(可选操作)。
boolean addAll(int index, Collection<? extends E> c)
将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。
List<E> subList(int fromIndex, int toIndex)
返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。
E get(int index)
返回列表中指定位置的元素。
int indexOf(Object o)
返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
4.3.4 练习1:测试常用方法
创建day12工程
创建cn.tedu.list包
创建Test1_List.java
4.4 ArrayList
4.4.1 概述
1) 存在于java.util包中。
2) 内部用数组存放数据,封装了数组的操作,每个对象都有下标。
3) 内部数组默认初始容量是10。如果不够会以1.5倍容量增长。
4) 查询快,增删数据效率会降低。

4.4.2 创建对象
new ArrayList():初始容量是10
4.4.3 练习1:测试常用方法
常用API,包括下标遍历,迭代器遍历
4.5 LinkedList
4.5.1 概述
双向链表,两端效率高。底层就是数组和链表实现的。

4.5.2 常用方法
add()
get()
size()
remove(i)
remove(数据)
iterator()
addFirst() addLast()
getFirst() getLast()
removeFirst() removeLast()
4.5.3 练习1:测试迭代器遍历
双向链表:下标遍历效率低,迭代器遍历效率高
4.6 扩展
4.6.1 ArrayList扩容
ArrayList相当于在没指定initialCapacity时就是会使用延迟分配对象数组空间,当第一次插入元素时才分配10(默认)个对象空间。假如有20个数据需要添加,那么会分别在第一次的时候,将ArrayList的容量变为10 (如下图一);之后扩容会按照1.5倍增长。也就是当添加第11个数据的时候,Arraylist继续扩容变为10*1.5=15(如下图二);当添加第16个数据时,继续扩容变为15 * 1.5 =22个
ArrayList没有对外暴露其容量个数,查看源码我们可以知道,实际其值存放在elementData对象数组中,那我们只需拿到这个数组的长度,观察其值变化了几次就知道其扩容了多少次。怎么获取呢?只能用反射技术了。
4.6.2 HashMap扩容
成长因子:

前面的讲述已经发现,当你空间只有仅仅为10的时候是很容易造成2个对象的hashcode 所对应的地址是一个位置的情况。这样就造成 2个 对象会形成散列桶(链表)。这时就有一个加载因子的参数,值默认为0.75 ,如果你hashmap的 空间有 100那么当你插入了75个元素的时候 hashmap就需要扩容了,不然的话会形成很长的散列桶结构,对于查询和插入都会增加时间,因为它要一个一个的equals比较。但又不能让加载因子很小,如0.01,这样显然是不合适的,频繁扩容会大大消耗你的内存。这时就存在着一个平衡,jdk中默认是0.75,当然负载因子可以根据自己的实际情况进行调整。
5.1 Set接口
5.1.1 概述
一个不包含重复元素的 collection。
数据无序(因为set集合没有下标)。
由于集合中的元素不可以重复。常用于给数据去重。
5.1.2 特点
Ø HashSet:底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中。当然K仍然不许重复。
Ø TreeSet:底层就是TreeMap,也是红黑树的形式,便于查找数据。
Ø HashMap实现中,当哈希值相同的对象,会在同一个hash值的位置存储不同属性的数据。

5.1.3 常用方法
boolean add(E e):添加元素。
boolean addAll(Collection c):把小集合添加到大集合中 。
boolean contains(Object o) : 如果此 collection 包含指定的元素,则返回 true。
boolean isEmpty() :如果此 collection 没有元素,则返回 true。
Iterator<E> iterator():返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。
int size() :返回此 collection 中的元素数。
Objec[] toArray():返回对象数组
5.1.4 练习1:测试常用方法
5.2 HashSet
5.2.1 概述
此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。
5.2.2 练习1:获取HashSet里的元素
5.2.3 练习2:Set存储属性值相同的对象
需求:我们仍然假设相同属性的两个人是同一个人
1、按照以前的经验,这种需求只需要重写equals()方法就可以实现。
2、但是我们提供以后,equals()根本就没有执行。问题出现在新增功能。
3、查找新增的源码发现,其实在添加时只是计算对象的hash值。
4、由于每次创建对象时hash值都不一样,所以每次都会当做新对象存起来。
5、所以,现在我们必须保证两个对象的hash值相同,重写hashCode()。

5.3 Map接口
5.3.1 概述
java.util接口 Map<K,V>
类型参数: K - 此映射所维护的键的类型V - 映射值的类型。
也叫哈希表、散列表。常用于存 键值对 结构的数据。其中的键不能重复,值可以重复.

5.3.2 特点
Ø 可以根据键 提取对应的值
Ø 键不允许重复,如果重复值会被覆盖
Ø 存放的都是无序数据
Ø 初始容量是16,默认的加载因子是0.75

5.3.3 继承结构

5.3.4 常用方法
void clear()
从此映射中移除所有映射关系(可选操作)。
boolean containsKey(Object key)
如果此映射包含指定键的映射关系,则返回 true。
boolean containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。
V get(Object key)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
boolean isEmpty()
如果此映射未包含键-值映射关系,则返回 true。
V put(K key, V value)
将指定的值与此映射中的指定键关联(可选操作)。
void putAll(Map<? extends K,? extends V> m)
从指定映射中将所有映射关系复制到此映射中(可选操作)。
V remove(Object key)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
int size()
返回此映射中的键-值映射关系数。
Set<Map.Entry<K,V>> entrySet()
返回此映射所包含的映射关系的 Set 视图。
5.3.5 练习1:测试常用方法
5.4 HashMap
Ø HashMap的键要同时重写hashCode()和equals()
hashCode()用来判断确定hash值是否相同
equals()用来判断属性的值是否相同
-- equals()判断数据如果相等,hashCode()必须相同
-- equals()判断数据如果不等,hashCode()尽量不同
5.4.1 概述
基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。
HashMap底层是一个Entry数组,当存放数据时会根据hash算法计算数据的存放位置。算法:hash(key)%n,n就是数组的长度。
当计算的位置没有数据时,就直接存放,当计算的位置有数据时也就是发生hash冲突的时候/hash碰撞时,采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。

5.4.2 练习1:读取HashMap的数据
5.4.3 练习2:字符串中的字符统计
接收用户输入的一串字符串,统计出现的每个字符的个数
5.5 Collections工具类
5.5.1 常用方法
Collections.sort(List<> list):根据元素的自然顺序 对指定列表按升序进行排序。
Collections.max():根据元素的自然顺序,返回给定 collection 的最大元素。
Collections.min():根据元素的自然顺序 返回给定 collection 的最小元素。
Collections.swap(List,i,j):在指定列表的指定位置处交换元素。
Collections.addAll():
5.5.2 测试