- 本笔记用作复习查看用,基础完整总结部分,基础不牢,地动山摇!
| 🔥Java帝国之行🔥 | 地址 |
| — | — |
| Java核心编程总结(一、继承) 🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/116209463 |
| Java核心编程总结(二、抽象类与接口)🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/116209487 |
| Java核心编程总结(三、多态与内部类)🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/116209507 |
| Java核心编程总结(四、异常与线程) 🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/116209529 |
| Java核心编程总结(五、线程池与死锁)🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/116209580 |
| Java核心编程总结(六、常用API与集合)🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/116209607 |
| Java核心编程总结(七、Stream流)🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/116209624 |
| Java核心编程总结(八、IO输入输出流)🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/116209648 |
| Java核心编程总结(九、File文件类)🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/116209674 |
| Java核心编程总结(十、反射) 🔥 | https://blog.csdn.net/Augenstern_QXL/article/details/117744497 |
===========================================================================
-
Object 类是 Java 中的祖宗类
-
一个类要么默认继承了 Object 类,要么间接继承了 Object 类
-
Object 类的方法是一切子类都可以直接使用的,所以我们要学习Object类的方法
Object 类常用方法
-
public String toString()
-
默认是返回当前对象在堆内存中的地址信息
-
直接输出对象名称,默认会调用 toString()方法,所以直接输出对象可以省略 toString()
-
所以 toString() 存在的意义是为了被子类重写,以便能够返回对象的数据内容输出
-
public boolean equals(Object o)
-
默认是比较两个对象的地址是否相同,相同返回true
-
直接比较两个对象的地址是否完全相同,可以用"=="替代equals
-
所以 equals 存在的意义是为了被子类重写
-
重写 Object 类的 toString() 方法以便返回对象的内容数据
-
右键 - generate - toString - OK
-
Objects类与Object还是继承关系
-
Objects类是 jdk 1.7 开始之后才有的
Objects 的方法
-
public static boolean equals(Object a, Object b)
-
比较两个对象
-
底层进行非空判断,从而可以避免空指针异常,更安全
-
public static boolean isNull(Object obj)
-
判断变量是否为 null , 为 null 返回 true
public class ObjectsDemo {
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student();
System.out.println(s1.equals(s2));
System.out.println(Objects.equals(s1,s2));
// false
// false
//询问s1是否为null,为null返回true
System.out.println(Objects.isNull(s1));
}
}
-
包:
java.util.Date
-
构造器
-
public Date()
创建当前系统的此刻日期时间对象 -
public Date(long time)
把时间毫秒值转换成日期对象 -
方法
-
public long getTime()
返回自1970年1月1日00:00:00 以来走过的毫秒值
public class DateDemo01 {
public static void main(String[] args) {
// a.创建一个日期对象代表了系统此刻日期时间对象
Date d = new Date();
System.out.println(d);
// Sun Jun 06 11:04:24 CST 2021
// b.拿当前日期对象的时间毫秒值
long time = d.getTime();
System.out.println(time);
// 1622948664752
// c.把时间毫秒值转换成日期对象
Date d1 = new Date(time);
System.out.println(d1);
// Sun Jun 06 11:04:24 CST 2021
}
}
DateFormat 作用
-
可以把 “日期对象” 或者 " 时间毫秒值" 格式化成我们喜欢的时间格式(格式化时间)
-
可以把字符串的时间形式解析成日期对象(解析字符串时间)
– DateFormat 是一个抽象类,不能直接使用,我们找它的子类简单日期格式化类: SimpleDateFormat
SimpleDateFormat
-
包:
java.text.SimpleDateFormat
-
构造器
-
public SimpleDateFormat(String pattern)
-
指定时间的格式创建简单日期格式化对象
-
成员方法
-
public String format(Date date)
:可以把日期对象格式化成我们喜欢的时间形式,返回的是字符串! -
public String format(Object time)
: 可以把时间毫秒值格式化成我们喜欢的时间形式,返回的是字符串! -
public Date parse(String date) throws ParseException
:把字符串的时间解析成日期对象
public class SimpleDateFormatDemo01 {
public static void main(String[] args) {
// 需求:把此刻日期对象格式化成我们喜欢的形式
// 1.得到此刻日期对象
Date d = new Date();
System.out.println(d);
//Wed Jan 27 17:35:55 CST 2021
// 2.创建一个简单日期格式化对象负责格式化日期对象
// 注意: 参数是时间的格式
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日 HH:mm:ss EEE a”);
// 3.开始调用方法格式化时间得到格式化字符串形式
String rs = sdf.format(d);
System.out.println(rs);
// 2021年01月27日 17:35:55 周三 下午
}
}
面试: 请问"2019-11-01 09:30:30" 往后1天15小时30分29s 后的时间是多少
public class SimpleDateFormatDemo03 {
public static void main(String[] args) throws ParseException {
// 面试题: 请问 "2019-11-01 09:30:30 "往后 1天15小时,30分29s 后的时间是多少
// a.定义一个字符串时间
String date = “2019-11-01 09:30:30”;
// b.把字符串的时间解析成Date日期对象
// 1.创建一个简单日期格式化对象负责解析字符串的时间成为日期对象
// 注意: 参数必须与被解析的时间的格式完全一致,否则执行报错
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
// 2.开始解析成日期对象
Date newDate = sdf.parse(date);
// c.得到日期对象的时间毫秒值 + 往后走1天15小时,30分29s
long time = newDate.getTime() + (24L6060 + 156060 +30*60 +29) * 1000;
// d.把时间毫秒值格式化成喜欢的字符串的时间形式
System.out.println(sdf.format(time));
}
}
-
Calendar 代表了系统此刻日期对应的日历对象
-
Calendar 是一个抽象类,不能直接创建对象
-
方法
-
public static Calendar getInstance()
:返回一个日历类的对象 -
public int get(int field)
: 取日期中的某个字段信息 -
public void set(int field,int value)
:修改日历的某个字段信息 -
public void add(int field,int value)
:添加日历的某个字段信息 -
public final Date getTime()
:拿到此刻日期对象 -
public long getTimeInMillis()
: 拿到此刻时间毫秒值
public class CalendarDemo01 {
public static void main(String[] args) {
// 1 通过调用日历类的静态方法getInstance得到一个当前此刻日期对象对应的日历对象
Calendar rightNow = Calendar.getInstance();
System.out.println(rightNow);
// 2.获取年
//public int get(int field)
int year = rightNow.get(Calendar.YEAR);
System.out.println(year);
// 获取月 月要+1 因为月从 0 开始
int mm = rightNow.get(Calendar.MONTH) + 1;
System.out.println(mm);
// 一年中的第几天
int days = rightNow.get(Calendar.DAY_OF_YEAR);
System.out.println(days);
// 4.修改日历的信息
// rightNow.set(Calendar.YEAR,2099);
// System.out.println(rightNow.get(Calendar.YEAR));
// 5.日历可以得到此刻日期对象
Date d = rightNow.getTime();
System.out.println(d);
// 6.此刻时间毫秒值
long time = rightNow.getTimeInMillis();
// 7.请问701天 15 小时后是哪个日期
// 让日历中的一年中的第几天往后走701天
rightNow.add(Calendar.DAY_OF_YEAR,701);
rightNow.add(Calendar.HOUR,15);
long time1 = rightNow.getTimeInMillis();
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日 HH:mm:ss EEE a”);
System.out.println(sdf.format(time1));
}
}
Math类中的方法全是静态方法,直接用类名调用即可
方法:
| 方法名 | 说明 |
| — | — |
| public static int abs(int a) | 获取参数a的绝对值 |
| public static double ceil(double a) | 向上取整 |
| public static double floor(double a) | 向下取整 |
| public static double pow(double a,double b) | 获取a的b次幂 |
| public static long round(double a) | 四舍五入取整 |
public class MathDemo01 {
public static void main(String[] args) {
// 1.取绝对值,返回正数
System.out.println(Math.abs(10)); // 10
System.out.println(Math.abs(-10.3)); // 10.3
// 向上取整
System.out.println(Math.ceil(4.000000001)); // 5.0
// 向下取整
System.out.println(Math.floor(4.999999)); // 4.0
// 求指数次方
System.out.println(Math.pow(2,3)); // 8.0
// 四舍五入
System.out.println(Math.round(4.4999)); // 4
}
}
– 静态方法
-
public static void exit(int status)
: 终止JVM虚拟机,非 0 是异常终止 -
public static long currentTimeMillis()
:获取当前系统此刻时间毫秒值 -
可以做数组的拷贝
-
arraycopy (Object var0, int var1 , Object var2,int var3,int var4 )
-
参数一: 原数组
-
参数二: 从原数组的哪个索引位置开始复制
-
参数三:目标数组
-
参数四:赋值到目标数组的哪个位置
-
参数五:赋值几个
public class SystemDemo {
public static void main(String[] args) {
System.out.println(“程序开始。。。。”);
// 1.终止当前虚拟机
// System.exit(0); //0代表正常终止
// 2.得到系统当前时间毫秒值
long time = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日 HH:mm:ss EEE a”);
System.out.println(sdf.format(time));
// 3.可以做数组的拷贝(了解)
int[] arrs1 = new int[]{10,20,30,40,50,60,70};
int[] arrs2 = new int[6]; // [0,0,0,0,0,0]
// arrs2 = [0,30,40,50,0,0]
System.arraycopy(arrs1,2,arrs2,1,3);
System.out.println(Arrays.toString(arrs2));
}
}
– 浮点型运算的时候直接 + - * / 可能会出现数据失真(精度问题)
BigDecimal 可以解决浮点型运算数据失真的问题
-
包:
java.math.BigDecimal
-
创建对象的方式
-
public static BigDecimal valueOf(double val)
: 包装浮点数成为大数据对象 -
方法
-
public BigDecimal add(BigDecimal value)
: 加法运算 -
public BigDecimal subtract(BigDecimal value)
:减法运算 -
public BigDecimal multiply(BigDecimal value)
:乘法运算 -
public BigDecimal divide(BigDecimal value)
:除法运算 -
public double doubleValue()
: 把BigDecimal 转换成 double 类型
public class BigDecimalDemo {
public static void main(String[] args) {
// 浮点型运算的时候直接+ * / 可能会出现数据失真(精度问题)。
System.out.println(0.1 + 0.2);
System.out.println(0.09 + 0.01);
System.out.println(1.0 - 0.32);
System.out.println(1.015 * 100);
System.out.println(1.301 / 100);
System.out.println("-------------------------");
double a = 0.1 ;
double b = 0.2 ;
double c = a + b ;
System.out.println©;
// 1.把浮点数转换成大数据对象运算
BigDecimal a1 = BigDecimal.valueOf(a);
BigDecimal b1 = BigDecimal.valueOf(b);
//BigDecimal c1 = a1.add(b1); // 加法
BigDecimal c1 = a1.divide(b1); // 除法
System.out.println(c1);
// 结果可能需要继续使用!!!
// BigDecimal只是解决精度问题的手段,double数据才是我们的目的!!
double rs = c1.doubleValue();
System.out.println(rs);
}
}
什么是包装类?
-
Java 认为一切皆对象,引入数据类型就是对象了
-
但是在Java 中 8 种 基本数据类型不是对象,只是表示一种数据的类型形式
-
Java 为了一切皆对象的思想统一,把8种基本类型转换成对应的类,这个类称为基本数据类型的包装类
| 基本数据类型 | 包装类(引用数据类型) |
| :-: | :-: |
| byte | Byte |
| short | Short |
| int | Integer(特殊) |
| long | Long |
| float | Float |
| double | Double |
| char | Character(特殊) |
-
自动装箱: 可以直接把基本数据类型的值或变量赋值给包装类
-
自动拆箱: 可以把包装类的变量直接赋值给基本数据类型
public class PackageClass {
public static void main(String[] args) {
int a = 12;
Integer a1 = 12; // 自动装箱
Integer a2 = a; // 自动装箱
double b = 99.9;
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
// 自动装箱
Double b1 = 99.9; // 自动装箱
Integer c= 100;
int c1 = c; // 自动拆箱
// int d = null; 报错
Integer d1 = null; //引用数据类型的默认值可以为null
Integer d2 = 0;
}
}
包装类的特殊功能
– Java 为包装类做了一些特殊功能
– 包装类作为类首先拥有 Object 类的方法
– 包装类作为引用类型的变量可以存储 null 值
- 可以把基本数据类型的值转换成字符串类型的值(没啥用)
-
调用
toString()
方法 -
调用
Integer.toString(基本数据类型的值)
得到字符串 -
直接把基本数据类型 + 空字符串 就得到了字符串
- 把字符串类型的数值转换成对应的基本数据类型的值(真的很有用)
-
Xxx.parseXxx("字符串类型的数值")
-
Xxx.valueOf("字符串类型的数值")
:推荐使用!
public class PackageClass02 {
public static void main(String[] args) {
// 1.把基本数据类型的值转成字符串
Integer it = 100 ;
// a.调用toString()方法。
String itStr = it.toString();
System.out.println(itStr+1);
// 1001
// b.调用Integer.toString(基本数据类型的值)得到字符串。
String itStr1 = Integer.toString(it);
System.out.println(itStr1+1);
//1001
// c.直接把基本数据类型+空字符串就得到了字符串。
String itStr2 = it+"";
System.out.println(itStr2+1);
//1001
// 2.把字符串类型的数值转换成对应的基本数据类型的值。(真的很有用)
String numStr = “23”;
//int numInt = Integer.parseInt(numStr);
int numInt = Integer.valueOf(numStr);
// Integer.valueOf 自动转int
System.out.println(numInt+1);
// 24
String doubleStr = “99.9”;
//double doubleDb = Double.parseDouble(doubleStr);
double doubleDb = Double.valueOf(doubleStr);
// Double.valueOf 自动转double
System.out.println(doubleDb+0.1);
}
}
======================================================================
什么是泛型?
-
泛型就是一个标签: <数据类型>
-
泛型可以在编译阶段约束只能操作某种数据类型
-
JDK 1.7开始之后,泛型后面的申明可以省略不写
-
泛型和集合都只能支持引用数据类型,不支持基本数据类型
ArrayList lists = new ArrayList();
ArrayList lists = new ArrayList<>();
// JDK 1.7开始之后,泛型后面的申明可以省略不写!!
- 泛型在编译阶段约束了操作的数据类型,从而不会出现类型转换异常
ArrayList lists = new ArrayList<>();
lists.add(“赵敏”);
lists.add(“张无忌”);
// lists.add(false); 报错
// lists.add(99.9); 报错
-
我们之前用的泛型都是别人写好的,接下来我们来自定义泛型类的使用
-
泛型类的概念:使用了泛型定义的类就是泛型类
-
格式:泛型变量建议使用 E,T,K,V
修饰符 class 类名<泛型变量>{}
- 泛型类的核心思想: 是把出现泛型变量的地方全部替换成传输的真实数据类型
– 需求:模拟ArrayList
集合自定义一个集合MyArrayList
集合
public static void main(String[] args) {
MyArrayList lists = new MyArrayList();
MyArrayList lists1 = new MyArrayList<>();
lists1.add(“java”);
lists1.add(“mysql”);
// list1.add(12.3); 报错,把出现泛型变量的地方全部替换成传输的String类型
lists1.remove(“java”);
System.out.println(lists1);
}
}
class MyArrayList{
private ArrayList lists = new ArrayList();
public void add(E e){
lists.add(e);
}
public void remove(E e){
lists.remove(e);
}
@Override
public String toString() {
return lists.toString();
}
}
什么是泛型方法?
- 定义了泛型的方法就是泛型方法
泛型方法的定义格式
-
修饰符 <泛型变量> 返回值类型 方法名称(形参列表){}
-
注意:方法定义了什么是泛型变量,后面就只能用什么泛型变量
– 需求:给你任何一个类型的数组,都能返回它的内容
public class GenericDemo {
public static void main(String[] args) {
Integer[] nums = {10 , 20 , 30 , 40 , 50};
String rs1 = arrToString(nums);
System.out.println(rs1);
String[] names = {“贾乃亮”,“王宝绿”,“陈羽凡”};
String rs2 = arrToString(names);
System.out.println(rs2);
}
public static String arrToString(T[] nums){
StringBuilder sb = new StringBuilder();
sb.append("[");
if(nums!=null && nums.length > 0){
for(int i = 0 ; i < nums.length ; i++ ){
T ele = nums[i];
sb.append(i == nums.length-1 ? ele : ele+", ");
}
}
sb.append("]");
return sb.toString();
}
}
什么是泛型接口?
- 使用了泛型定义的接口就是泛型接口
泛型接口的格式
- 修饰符 interface 接口名称<泛型变量> {}
通配符:?
?
可以用在使用泛型的时候代表一切类型
E
T
K
V
是在定义泛型的时候使用代表一切类型
泛型的上下限:
<? extends Car>
: 那么?必须是Car或者其子类(泛型的上限)
<?super Car>
: 那么?必须是Car或者其父类(泛型的下限,不是很常见)
集合重点
================================================================================
什么是集合?
-
集合是一个大小可变的容器
-
容器中的每个数据称为一个元素。 数据 == 元素
集合与数组的区别
-
集合中类型可以不确定,大小可以不固定
-
数组中类型和长度一旦定义出来就都固定了
集合的作用
-
在开发中,很多时候元素的个数是不确定的
-
而且要经常进行元素的增删改查操作,集合都是非常合适的
-
Collection
集合 是Java中集合的祖宗类,Collection
集合的功能是一切集合都可以直接使用的 -
包:
import java.util.Collection
-
public boolean add(E e)
: 把给定的元素添加到当前集合中 -
public void clear()
: 清空集合中所有的元素 -
public boolean remove(E e)
: 把给定的对象在当前集合中删除 -
public boolean contains(Object obj)
: 判断当前集合中是否包含给定的对象 -
public boolean isEmpty()
: 判断当前集合是否为空 -
public int size()
: 返回集合中元素的个数 -
public Object[] toArray()
: 把集合中的元素,储存到数组中
public class CollectionDemo {
public static void main(String[] args) {
// HashSet:添加的元素是无序,不重复,无索引。
Collection sets = new HashSet<>();
// 1.添加元素,添加成功返回true.
System.out.println(sets.add(“贾乃亮”)); // true
System.out.println(sets.add(“贾乃亮”)); // false
System.out.println(sets.add(“王宝强”)); // true
sets.add(“陈羽凡”);
System.out.println(sets); // 集合重写了toString()方法,默认打印出内容信息
// [贾乃亮, 王宝强, 陈羽凡]
// 2.清空集合的元素。
//sets.clear();
//System.out.println(sets);
// 3.判断集合是否为空 是空返回true 反之
System.out.println(sets.isEmpty()); // false
// 4.获取集合的大小
System.out.println(sets.size()); // 3
// 5.判断集合中是否包含某个元素 。
System.out.println(sets.contains(“贾乃亮”));
// 6.删除某个元素:如果有多个重复元素默认删除前面的第一个!
sets.remove(“陈羽凡”);
System.out.println(sets);
// 7.把集合转换成数组
Object[] arrs = sets.toArray();
System.out.println(“数组:”+ Arrays.toString(arrs));
String[] arrs1 = sets.toArray(String[]::new); // 以后再了解,指定转换的数组类型!
System.out.println(“数组:”+ Arrays.toString(arrs1));
System.out.println("---------------------拓展---------------------------");
Collection c1 = new ArrayList<>();
c1.add(“李小璐”);
c1.add(“马蓉”);
Collection c2 = new ArrayList<>();
c2.add(“白百合”);
c1.addAll(c2); // 把c2集合的元素全部倒入到c1
System.out.println(c1);
}
}
– Collection 集合的遍历方式有三种:
-
迭代器
-
foreach(增强for循环)
-
JDK 1.8 开始的新技术 Lambda 表达式(了解)
迭代器遍历集合
-
方法
-
public Iterator iterator()
: 获取集合对应的迭代器,用来遍历集合中的元素的 -
E next()
: 获取下一个元素值 -
boolean hasNext()
: 判断是否有下一个元素,有返回true,反之 -
流程
- 先获取当前集合的迭代器
Iterator<String> it = lists.iterator()
- 定义一个 while 循环, 问一次取一次
-
通过
it.hasNext()
询问是否有下一个元素,有就通过 -
it.next()
取出下一个元素
public class CollectionDemo{
public static void main(String[] args) {
Collection lists = new ArrayList<>();
lists.add(“赵敏”);
lists.add(“小昭”);
lists.add(“殷素素”);
lists.add(“周芷若”);
System.out.println(lists);
// lists = [赵敏, 小昭, 殷素素, 周芷若]
// 1.得到集合的迭代器对象。
Iterator it = lists.iterator();
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next()); // 出现异常NoSuchElementException,出现没有此元素异常!
// 2.使用while循环遍历。
while(it.hasNext()){
String ele = it.next();
System.out.println(ele);
}
}
}
foreach
遍历
-
foreach 是一种遍历形式,可以遍历集合或者数组
-
foreach 遍历集合实际上是迭代器遍历的简化写法
-
foreach 遍历的关键是记住格式
for(被遍历集合或者数组中元素的类型 变量名称:被遍历集合或者数组){
}
public class CollectionDemo{
public static void main(String[] args) {
Collection lists = new ArrayList<>();
lists.add(“赵敏”);
lists.add(“小昭”);
lists.add(“殷素素”);
lists.add(“周芷若”);
System.out.println(lists);
// lists = [赵敏, 小昭, 殷素素, 周芷若]
for (String ele : lists) {
System.out.println(ele);
}
int[] ages = new int[]{17 , 18 , 38 , 21};
for (int age : ages) {
System.out.println(age);
}
}
}
– 缺点:foreach 遍历无法知道遍历到了哪个元素,因为没有索引
Lambda(暂时了解)
- jdk 1.8 开始之后的新技术
public class CollectionDemo03 {
public static void main(String[] args) {
Collection lists = new ArrayList<>();
lists.add(“赵敏”);
lists.add(“小昭”);
lists.add(“殷素素”);
lists.add(“周芷若”);
System.out.println(lists);
// [赵敏, 小昭, 殷素素, 周芷若]
// s
lists.forEach(s -> {
System.out.println(s);
});
// lists.forEach(s -> System.out.println(s));
// lists.forEach(System.out::println);
}
}
– Set系列集合:添加的元素是无序,不重复,无索引
-
HashSet
:添加的元素是无序,不重复,无索引的 -
LinkedHashSet
: 添加的元素是有序,不重复,无索引的 -
TreeSet
: 不重复,无索引,按照大小默认升序排列
public class CollectionDemo01 {
public static void main(String[] args) {
// HashSet:添加的元素是无序,不重复,无索引的。
// 多态写法
Collection sets = new HashSet<>();
sets.add(“MyBatis”);
sets.add(“Java”);
sets.add(“Java”);
sets.add(“Spring”);
sets.add(“MySQL”);
sets.add(“MySQL”);
// [Java, MySQL, MyBatis, Spring]
System.out.println(sets);
}
}
4.3.1 HashSet
HashSet
:添加的元素是无序,不重复,无索引的
public class CollectionDemo01 {
public static void main(String[] args) {
// 无序,不重复,无索引的。
Set sets = new HashSet<>(); // 一行经典代码!!
sets.add(“Mybatis”);
sets.add(“Java”);
sets.add(“Java”);
sets.add(“MySQL”);
sets.add(“MySQL”);
sets.add(“Spring”);
// [Java, MySQL, Spring, Mybatis]
System.out.println(sets);
}
}
面试热点:Set 集合添加的元素是不重复的,是如何去重复的
-
对于有值特性的,
Set
集合可以直接判断进行去重复 -
对于引用数据类型的类对象,Set 集合是按照如下流程进行是否重复的判断
-
引用数据类型:自己定义的类型,比如自己定义的苹果apple
-
Set
集合会让两对象先调用自己的hashCode()
方法得到彼此的哈希值(所谓的内存地址) -
然后比较两个对象的哈希值是否相同,如果不相同则直接认为两个对象不重复
-
如果哈希值相同,会继续让两个对象进行
equals
比较内容是否相同,如果相同认为重复,如果不相同认为不重复
面试热点:Set系列集合元素无序的根本原因(面试必考)
-
Set 系列集合添加元素无序的根本原因是因为底层采用了哈希表存储元素
-
JDK1.8 之前:哈希表 = 数组 + 链表 + (哈希算法)
-
JDK1.8 之后:哈希表 = 数组 + 链表 + 红黑树 + (哈希算法)
-
当链表长度超过 8 时,将链表转换为红黑树,这样大大减少了查找时间
Set 系列集合是基于哈希表存储数据的,它的增删改查的性能都很好,但是它是无序不重复的。
4.4.2 LinkedHashSet
-
是
HashSet
的子类,元素是 有序,不重复,无索引 -
LinkedHashSet
底层依然是使用哈希表存储元素的,但是每个元素都额外带一个链来维护添加顺序 -
不光增删查快,还有序
-
缺点是多了一个存储顺序的链会占内存空间,而且不允许重复,无索引
public static void main(String[] args) {
// 有序不重复无索引
Set sets = new LinkedHashSet<>();
sets.add(“Mybatis”);
sets.add(“Java”);
sets.add(“Java”);
sets.add(“MySQL”);
sets.add(“MySQL”);
sets.add(“Spring”);
// [Java, MySQL, Spring, Mybatis]
System.out.println(sets);
}
}
4.4.3 TreeSet
– TreeSet
: 不重复,无索引,按照大小默认升序排序
– TreeSet 集合称为排序不重复集合,可以对元素进行默认的升序排序
TreeSet 集合自排序的方式
-
有值特性的元素直接可以升序排序(整型,浮点型)
-
字符串类型的元素会按照首字符的编号排序
-
对于自定义的引用数据类型,TreeSet 默认无法排序,执行的时候直接报错,因为人家不知道排序规则
自定义的引用数据类型的排序实现
– 对于自定义的引用数据类型,TreeSet 默认无法排序
– 所以我们需要定制排序的大小规则,方案有两种
-
直接为对象的类实现比较器规则接口
Comparable
,重写比较方法 -
直接为集合设置比较器
Comparator
对象,重写比较方法 -
如果类和集合都存在大小规则,默认使用集合自带的规则进行大小排序!
– List系列集合:添加的元素是有序,可重复,有索引
-
ArrayList
: 添加的元素是有序,可重复,有索引 -
LinkedList
: 添加的元素是有序,可重复,有索引 -
Vector
是线程安全的,速度慢,开发中很少使用
-
List 集合继承了 Collection 集合的全部功能,同时因为 List 系列集合有索引,所以多了很多按照索引操作元素的功能
-
开发中
ArrayList
集合用的最多,查询快,增删慢,是基于数组储存数据的
4.4.1 ArrayList 集合
开发中ArrayList集合用的最多!
方法:
-
public void add(int index,E element)
: 将指定的元素,添加到该集合中的指定位置上 -
public E get(int index)
: 返回集合中指定位置的元素 -
public E remove(int index)
: 移除列表中指定位置的元素,返回的是被移除的元素 -
public E set(int index,E element)
: 用指定元素替换集合中指定位置的元素,返回更新前的元素值
public class ListDemo01 {
public static void main(String[] args) {
// 1.创建一个ArrayList集合对象:这是一行经典代码!
// List:有序,可重复,有索引的。
// 多态写法: 将子类对象给父类类型的接口
List lists = new ArrayList<>();
lists.add(“java1”);
lists.add(“java1”);
lists.add(“java2”);
lists.add(“java2”);
System.out.println(lists);
// [java1, java1, java2, java2]
// 2.在某个索引位置插入元素。
lists.add(2,“MySQL”);
System.out.println(lists);
// [java1, java1, MySQL, java2, java2]
// 3.根据索引删除元素,返回被删除元素
System.out.println(lists.remove(2));
// MySQL
System.out.println(lists);
// [java1, java1, java2, java2]
// 4.根据索引获取元素
System.out.println(lists.get(2));
// java2
// 5.修改索引位置处的元素
lists.set(3,“Mybatis”);
System.out.println(lists);
// [java1, java1, java2, Mybatis]
}
}
4.4.2 List集合的遍历方式
-
4种
-
List 系列集合多了索引,所以多了一种按照索引遍历集合的 for 循环
-
for循环
-
迭代器
-
foreach
-
jdk 1.8 之后的 Lambda 表达式
public class ListDemo02 {
public static void main(String[] args) {
// 1.这是一行经典代码
// 将子类对象给父类接口,多态写法
List lists = new ArrayList<>();
lists.add(“java1”);
lists.add(“java2”);
lists.add(“java3”);
/** (1)for循环。 */
for(int i = 0 ; i < lists.size() ; i++ ) {
String ele = lists.get(i);
System.out.println(ele);
}
System.out.println("-----------------------");
/** (2)迭代器。 */
Iterator it = lists.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
System.out.println("-----------------------");