集合
概念
java中的集合就是用来替换掉定长数组的一个更高级的容器
和数组的区别
优点
1.长度不用在定义的时候就定死
2.一个集合可以存多种不同类型的数据
3.封装了多种对集合中的元素进行操作的方法
总体框架
java的集合总体分2种,一类是实现Collection接口;另一类是实现Map接口。
Collection是单列集合,而Map是双列(key-value)集合
单列集合下的实现类分2种,Set(无序不重复)和List(有序可重复)
List
List 接口的实现类 ArrayList
是个动态数组,与普通的数组相比,ArrayList的大小是可变的,可以根据需要动态增长或缩小。
初始默认容量为10,容量不足的时候会自动扩容至1.5倍的大小
非线程安全
缺点:
由于ArrayList的底层是数组结构,所以ArrayList的查询效率高,可通过下标直接查询,
但是他的添加和删除效率低,原因是因为添加和删除操作要涉及到数据的迁移。
ArrayList的遍历
1.迭代器遍历
迭代器初始状态不指向任何元素
第一次使用next()方法后先移向第一个元素,然后获取到这个元素。
用next()方法前先用hasNext()方法判断是否有下一个元素,否则如果下一个位置没有元素时使用了next()方法就会报异常
迭代器遍历代码:
2.增强for遍历(常用)
增强for循环代码,在idea中用"集合名称.for"就能打出以下代码
ArrayList底层结构
1.ArrayList底层是用一个object类型的数组实现的
2.在定义时可在构造器里写集合的初始容量,这个容量就是底层数组的大小
3.如果没有填初始容量,那么初始时集合大小为0,当第一次使用add方法时数组大小变成10
4.当数组容量达到10,自动扩容1.5倍变成15
List 接口的实现类 Vector
与ArrayList除了扩容机制不同外,另一特点是线程安全
List 接口的实现类 LinkedList
底层是双向链表
每个节点(Node对象),里面维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终完成双向链表;
所以添加和删除元素的效率较高
Set
Set下的接口的共有特点:无序不重复
遍历方式与List一致,但是不能通过索引获取组内元素
Set接口的实现类HashSet
- 底层是数组+链表+红黑树
- HashSet实现了Set接口;
- 可以存放 null 值,但是只能有一个null;
- HashSet 不保证元素是有序的,取决于hash后,再确定索引的结果;
- 不能有重复元素 / 对象;
- 在Java8中,如果一条链表的元素个数达到 TREEIFY_THRESHOLD(默认是8),并且table大小>=MIN_TREEIFY_CAPACITY(默认是64),就会进行树化(红黑树);
放入元素时,先用集合内对象的hashCode()计算出该元素应该放在哪个位置,
如果这个位置没有元素就直接放入;
这个位置有元素就再调用对象的equals()方法比较该元素以及以该元素为头节点的链表里面的所有元素,如果有一个元素比较时返回了true,就不添加新元素。如果都为false就将新元素放在末尾。
如果集合中的类没有重写hashCode()和equals()方法,默认使用Object类的这2个方法,计算和比较的都是地址值
HashSet的子类LinkedHashSet
LinkedHashSet继承了HashSet,是HashSet的子类
与HashSet的区别是LinkedHashSet里面的元素有序且不重复
底层是用数组+双向链表
还是先用hashCode()方法算出索引,再使用equals()方法判断是否要插入
Map
概述
Map接口和Collection接口并列,Collection接口是单列,而Map接口是双列,用于保存具有映射关系的数据:Key - Value;
Key 和 Value 可以是任何引用类型的数据
Key不可以重复,Value可以重复
Map 的 Key 可以为 null,value 也可以为 null,但 key 为 null 只能有一个;
Key 和 Value 之间存在单向一对一关系,即通过指定的 Key 总能找到对应的 Value
但不能直接通过Value找到Key
当插入新数据时如果这个新数据的key在map中已经存在,那么这个新元素的value就会覆盖原有的value
map接口常用方法
- put :添加
- remove : 根据键删除映射关系
- get : 根据键获取值
- size : 获取元素个数
- isEmpty : 判断个数是否为0
- clear : 清除
- containsKey : 查找键是否存在
Map的遍历
public static void main(String[] args) {
Map map = new HashMap();
map.put("小明","百草味");
map.put("小龙","蒲公英");
map.put("小马","西兰花");
//第一种:先取出所有的Key,通过Key取出对应的value
Set keySet = map.keySet();
//增强for
for(Object key : keySet){
System.out.println(key+" - "+map.get(key));
}
//第二种:把所有的value取出
Collection values = map.values();
//然后遍历Collection就行
//增强for
for(Object value : values){
System.out.println(value);
}
//第三种:通过EntrySet来获取
Set entrySet = map.entrySet();
//(1)增强for
for(Object entry : entrySet){
//将entry转成map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey()+" - "+m.getValue());
}
}
Map的实现类HashMap
HashMap 底层维护了 Node 类型的数组 table ,默认为 null;
当创建对象时,将加载因子(loadfactor)初始化为0.75;
当添加 key-value 时,通过 key 的哈希值得到在 table的索引,然后判断该索引处是否有元素,如果没有元素则直接添加。如果该索引处有元素,继续判断该元素的 key 是否和准备加入的 key 相等,如果相等,则直接替换 value;如果不相等,则需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容。(扩容机制和HashSet完全一样,因为HashSet底层就是HashMap)
第一次添加,会扩容 table 容量为16,临界值(threshold)为12;
以后再扩容,会扩容 table 容量为原来的2倍,临界值为原来的2倍,即24,以此类推;
在Java8中,如果一条链表的元素个数超过 TREEIFY_THRESHOLD(默认是8),并且 table的大小>= MIN_CAPACITY(默认是64),就会进行树化(红黑树);
异常处理
概念
程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常
Java程序在执行过程中所发生的异常事件可分为两类:
Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError(栈溢出)和OOM(内存溢出)。一般不编写针对性的代码进行处理。
Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:
空指针访问
试图读取不存在的文件
网络连接中断
数组越界
javac
是 Java 编译器的命令行工具,用于将 Java 源代码文件(以 .java 扩展名结尾)编译成 Java 字节码文件(以 .class 扩展名结尾)。Java 编译器将源代码转换为字节码,这些字节码可以在 Java 虚拟机(JVM)上运行。
检查异常
代码还没运行前,编译器就会检查你的代码,会不会出现异常,要求你对可能出现的异常必须做出相应的处理。
处理检查异常
1.通过throws Exception抛出,一直抛到java虚拟机来处理。
2.用try...catch捕获
检查异常类型
除了RuntimeException与其子类,以及错误(Error),其他的都是检查异常
非检查异常
编译器不要求强制处置的异常,虽然你有可能出现错误,但是编译器不会在编译的时候检查
对于这些异常,我们应该修正代码,而不是去通过异常处理器处理。这样的异常发生的原因多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。
public class a7_AllDemo {
public static void main(String[] args) {
CMDCalculate();
}
public static void CMDCalculate() {
Scanner scan = new Scanner(System.in);
int num1 = scan.nextInt();
int num2 = scan.nextInt();
int result = devide(num1, num2);
System.out.println("result:" + result);
scan.close();
}
public static int devide(int num1, int num2) {
return num1 / num2;
}
}
常见异常
java.lang.RuntimeException: 运行时异常
ClassCastException: 类类型转换异常,当试图将对象强制转换为不是实例的子类时,抛出该异常;
ArrayIndexOutOfBoundsException: 数组下标越界异常,当你使用不合法的索引访问数组时会抛出该异常;
NullPointerException: 空指针异常,通过null进行方法和属性调用会抛出该异常;
ArithmeticException: 算术运算异常,除数为0,抛出该异常;
NumberFormatException: 数字转换异常,当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常;
InputMismatchException: 输入不匹配异常,输入的值数据类型与设置的值数据类型不能匹配。
异常处理的处理机制
在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行x/y运算时,要检测分母为0,数据为空,输入的不是数据而是字符等。过多的if-else分支会导致程序的代码加长、臃肿,可读性差。将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、并易于维护。因此采用异常处理机制。
在编写代码处理异常时,对于检查异常/非检查异常,都有2种不同的处理方式:
1、使用try...catch...finally语句块处理它。
2、在函数签名中使用throws 声明交给函数调用者去解决。
异常处理:try-catch-finally
语法格式:
try{
...... //可能产生异常的代码
}
catch( ExceptionName1 e ){
...... //当产生ExceptionName1型异常时的处置措施
}
catch( ExceptionName2 e ){
...... //当产生ExceptionName2型异常时的处置措施
}
finally{
...... //无论是否发生异常,都无条件执行的语句
}
finally主要做一些清理工作,如流的关闭,数据库连接的关闭等
异常处理:throws
throws是另一种处理异常的方式,它不同于try...catch...finally,throws仅仅是将函数中可能出现的异常向调用者声明,而自己则不具体处理。
格式就是在方法后面加throws 异常类型1,异常类型2 . . .