Java第三周预习
文章目录
一、常用API
1.1API是什么
API(Application Programming interface) 应用程序编程接口。
简单来说就是Java帮我们写好的一些类和方法可以直接拿来使用。
1.2Object类
每个类都是Object的子类,都可以直接使用Object类中的方法
Object类常用方法
toStrings方法
//返回该对象的字符串表示,用来返回更易识别的字符串
public String toString(){
return getClass().getName() + "@" +Integer.toHexString(hashCode());
}
/*
getclass().getName():类名称,全类名(包名 + 类名)
Integer.toHexString():转十六进制
hashCode():返回的是对象内存地址 + 哈希算法,算出来的整数(哈希值)
注意:使用打印方法,打印对象名的时候,printfln方法,源码层面会自动调用toString()方法
可以重写toString方法
*/
equals方法
//对象之间的比较,返回true或者false
public boolean equals(Object obj){
return (this == obj);
}
//Object类中的equals方法默认比较的是对象地址
//通常会重写equals方法,让对象之间比较内容
1.3Math类
包含执行基本数字运算的方法
Math的常用方法
方法 | 作用 |
---|---|
public static int abs (int a) | 取参数绝对值 |
public static double ceil (double a) | 向上取整 |
public static double floor (double a) | 向下取整 |
public static int round (float a) | 四舍五入 |
public static int max (int a,int b) | 取两个参数较大值 |
public static double pow (double a,double b) | 返回a的b次幂的值 |
public static double random () | 返回为double的随机值,范围[0.0,1.0) |
1.4System类
System的常用方法
方法 | 作用 |
---|---|
public static void exit (int status) | 终止当前运行的Java虚拟机,非零表示异常终止 |
public static long currentTimeMillis () | 返回1970年1月1日0时0分0秒到现在经过的毫秒值 |
public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length) | 数组拷贝(数据源数组、起始索引、目的地数组、起始索引、拷贝个数) |
1.5BigDecimal类
用于解决小数运算中,出现的不精确的问题
创建BigDecimal对象
public BigDecimal(double val)//不推荐,无法保证小数运算的精确
public BigDecimal(String val)
public static BigDecimal valueOf(double val)
------------------------------------------------
BigDecimal bd = new BigDecimal(0.1);//不推荐,无法保证小数运算的精确
BigDecimal bd = new BigDecimal("0.1");
BigDecimal bd = BigDecimal.balueOf(0.1);
BigDecimal常用方法
方法声明 | 功能描述 |
---|---|
public BigDecimal add(BigDecimal b) | 加法a+b |
public BigDecimal subtract(BigDecimal b) | 减法a-b |
public BigDecimal multiply(BigDecimal b) | 乘法a*b |
public BigDecimal divide(BigDecimal b) | 除法a/b |
public BigDecimal divide(另一个BigDecimal对象,精确几位,舍入模式) | 除法a/b |
注意:除不尽会出现异常。
- RoundingMode.UP 进一法
- RoundingMode.DOWN 去尾法
- RoundingMode.HALF_UP 四舍五入
1.6时间类与日历类(JDK8-)
Date类
构造方法:
public Date();//把当前时间封装为Date
public Date(long time);//把时间毫秒值转换成Date对象(时间原点+time)
常用方法:
public void getTime();//返回从时间原点到(对象)此刻的总毫秒数
public void setTime(long time);//设置日期对象的时间为当前时间毫秒值对应的时间(修改对象)
//代码使用例:
import java.util.*;
public class Main {
public static void main(String[] args){
Date d1 = new Date();
Date d2 = new Date(1000L);
Date d3 = new Date();
System.out.println(d1);//输出现在时间
System.out.println(d2);//输出时间原点+1000毫秒
System.out.println(d1.getTime());//输出时间原点到d1(此刻)经过毫秒
System.out.println(d2.getTime());//输出时间原点到d2经过多少毫秒
d3.setTime(0L);//将d3设置为时间原点+0L
System.out.println(d3);//输出时间原点+0毫秒
}
}
SimpleDateForma类
用于日期格式化
构造方法:
public SimpleDateFormat();//创建一个日期格式化对象,使用[默认模式]
public SimpleDateFormat(String pattern);//创建一个日期格式化对象,使用[手动指定模式]
常用方法:
public final String format(Date date);//将日期对象,转换为字符串
public public final Date parse(String source);//将日期字符串,解析为日期对象
//fotmat方法代码使用例:
import java.util.*;
public class Main {
public static void main(String[] args){
SimpleDateFormat s1 = new SimpleDateFormat();
SimpleDateFormat s2 = new SimpleDateFormat("yyyy年MM月dd日 HH:mm;ss E a");
Date d = new Date();
String result1 = s1.format(d);
String result2 = s2.format(d);
System.out.println(result1);//输出2024/4/4 15:40
System.out.println(result2);//输出2024年04月04日 15:40;03 周四 下午
}
}
//parse方法使用例
public class Main {
public static void main(String[] args) throws ParseException {
String day = "2024年4月1日";
SimpleDateFormat s = new SimpleDateFormat("yyyy年MM月dd日");
Date d = s.parse(day); //会有异常
System.out.println(d);
}
}
日历类
Calendar类代表的事系统此刻时间对应的日历,通过它可以单独获取、修改时间中的年、月、日、时、分
秒等。
创建对象:
public static Calend getInstance();//获取当前时间的日历对象
import java.util.*;
public class Main {
public static void main(String[] args) throws ParseException {
Calendar c = Calendar.getInstance();
//Calendar c : 抽象类
//Calendar.getInstance()获取子类对象
System.out.println(c);
}
}
//输出java.util.GregorianCalendar[time=1712286471889,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=1,ERA=1,YEAR=2024,MONTH=3,WEEK_OF_YEAR=14,WEEK_OF_MONTH=1,DAY_OF_MONTH=5,DAY_OF_YEAR=96,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=11,HOUR_OF_DAY=11,MINUTE=7,SECOND=51,MILLISECOND=889,ZONE_OFFSET=28800000,DST_OFFSET=0]
常用方法:
public int get(int field);//取日历中的某个字段信息
public void set(int field ,int value);//修改日历的某个字段信息
public void add(int field,int almount);//为某个字段增加/减少指定的值
//get方法使用
import java.util.*;
public class Main {
public static void main(String[] args){
Calendar c = Calendar.getInstance();
//Calendar c : 抽象类
//Calendar.getInstance()获取子类对象
int year = c.get(Calendar.YEAR);
System.out.println(year);
//获取年份
int month = c.get(Calendar.MONTH);
System.out.println(month+1);
//Calendar类的月份是0~11
int day = c.get(Calendar.DAY_OF_MONTH);
System.out.println(day);
//获取月中日期
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);
char [] week = {' ','日','一','二','三','四','五','六'};
System.out.println(week[dayOfWeek]);
//星期:日 一 二 三 四 五 六
// 1 2 3 4 5 6 7
//其他使用例
int hour = c.get(Calendar.HOUR_OF_DAY);
System.out.println(hour);
int minute = c.get(Calendar.MINUTE);
System.out.println(minute);
int second = c.get(Calendar.SECOND);
System.out.println(second);
int millisecond = c.get(Calendar.MILLISECOND);
System.out.println(millisecond);
int dayOfYear = c.get(Calendar.DAY_OF_YEAR);
System.out.println(dayOfYear);
}
}
//set方法使用
import java.util.*;
public class Main {
public static void main(String[] args) {
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR,2020);
System.out.println(c.get(Calendar.YEAR));
//输出2020
//选择修改
c.set(2012,1,5);
System.out.println(c.get(Calendar.YEAR));
//输出2012
//按照年月日时分秒修改
}
}
//add方法使用
import java.util.*;
public class Main {
public static void main(String[] args) {
Calendar c = Calendar.getInstance();
c.add(Calendar.YEAR,1);
System.out.println(c.get(Calendar.YEAR));
//输出2025,正数增长负数减少
}
}
1.7时间类与日历类(JDK8+)
日历类
- LocalDate :代表本地日期(年月日星期)
- LocalTime :代表本地时间(时分秒纳秒)
- LocalDateTime :代表本地日期时间(年月日星期时分秒纳秒)
创建对象:
public static Xxxx now();//获取系统当前时间对应对象
public static Xxxx of(...);//获取指定时间的对象
//创建对象实例
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.LocalDate;
public class Main {
public static void main(String[] args) {
LocalDate ld = LocalDate.now();
LocalTime lt = LocalTime.now();
LocalDateTime ldt = LocalDateTime.now();
LocalDate ld1 = LocalDate.of(2077,12,12);
LocalTime lt1 = LocalTime.of(12,12,12);
LocalDateTime ldt1 = LocalDateTime.of(2077,12,12,12,12);
}
}
//不同对象之间的转换
toLocalDate();
toLocalTime();
//使用
LocalDate ld = now.toLocalTime();
LocalTime lt = now.toLocalDate();
使用get()方法获取具体时间
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.LocalDate;
public class Main {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
System.out.println(now.getYear() + "年");
System.out.println(now.getMonth() + "月");
System.out.println(now.getMonthValue() + "月");
System.out.println(now.getDayOfMonth() + "日");
System.out.println(now.getHour() + "时");
System.out.println(now.getMinute() + "分");
System.out.println(now.getSecond() + "秒");
System.out.println(now.getNano() + "纳秒");
}
}
/*输出
2024年
APRIL月
4月
5日
13时
57分
56秒
653961300纳秒
*/
修改对象
**注意:**这三个对象都是不可变的,修改方法返回的都是新的对象
方法名 | 说明 |
---|---|
withHour、withMinute、withSecond、withNano | 修改时间,返回新时间对象 |
plusHours、plusMinutes、plusSeconds、plusNanos | 把某个信息加多少,返回新时间对象 |
minusHours、minusMinutes、minusSeconds、minusNanos | 把某个信息减多少,返回新时间对象 |
equals isBefore isAfter | 判断2个时间对象,是否相等,在前还是在后 |
日期格式化类
获取格式化对象:
static DateTimeFormatter ofParttern(格式);
DateTimeFormatter f = DateTimeFormatter.ofPattern("...");
格式化:
Sring format(时间对象);//按照指定方式格式化
解析:
LocalDateTime.parse("解析字符串",格式化对象);
LocalDate.parse("解析字符串",格式化对象);
LocalTime.parse("解析字符串",格式化对象);
//代码使用例
public class Main {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
System.out.println("格式化之前:" + now);
//获取格式化对象
DateTimeFormatter Formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
//格式化
String result = now.format(Formatter);
System.out.println("格式化之后:" + result);
//解析
String time = "2077年04月05日";
LocalDate parse = parse(time,Formatter);
System.out.println("解析之后:" + parse);
}
}
/*输出
格式化之前:2024-04-05T14:34:28.622856900
格式化之后:2024年04月05日
解析之后:2077-04-05
*/
时间类
Instant类
public class Main {
public static void main(String[] args) {
Instant now = Instant.now();
System.out.println("当前的时间戳是:" + now);
ZonedDateTime ZonedDateTime = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("当前的时区时间是:" + ZonedDateTime);
}
}
/*输出:
当前的时间戳是:2024-04-05T07:46:18.306572500Z
当前的时区时间是:2024-04-05T15:46:18.310262+08:00[Asia/Shanghai]
*/
Instant方法:
方法名 | 说明 |
---|---|
static Instant now() | 获取当前时间的Instant对象(标准时间) |
static Instant ofXxxx(long epochMilli) | 根据(秒/毫秒/纳秒)获取Instant对象 |
ZonedDateTime atZone(ZoneId zone) | 指定时区 |
boolean isXxx(Instant otherInstant) | 判断系列的方法 |
Instant minusXxx(long millisToSubtract) | 减少时间系列的方法 |
Instant plusXxx(long millisToSubtract) | 增加时间系列的方法 |
ZoneId类
static Set<String> getAvailableZoneIds():获取Java中支持的所有时区
static ZoneId systemDefault():获取系统默认时区
static ZoneId of(String zoneId):获取一个指定时区
public static void main(String[] args) {
//获取Java中支持的所有时区
Set<String> set = ZoneId.getAvailableZoneIds();
System.out.println(set);
System.out.println(set.size());
System.out.println("================================");
//获取系统默认时区
ZoneId zoneId = ZoneId.systemDefault();
System.out.println(zoneId);
System.out.println("================================");
//获取一个指定时区
ZoneId of = ZoneId.of("Africa/Nairobi");
System.out.println(of);
ZonedDateTime zonedDateTime = Instant.now().atZone(of);
System.out.println(zonedDateTime);
System.out.println("================================");
}
/*输出为:
[Asia/Aden, America/Cuiaba, Etc/GMT+9, Etc/GMT+8,....(此处省略),Europe/Monaco]
603
================================
Asia/Shanghai
================================
Africa/Nairobi
2024-04-05T10:59:54.238216400+03:00[Africa/Nairobi]
*/
ZoneDataTime方法:
方法名 | 说明 |
---|---|
static ZonedDateTime now | 获取当前时间的ZonedDateTime对象 |
static ZonedDateTime ofXxxx(…) | 获取指定时间的ZonedDateTime对象 |
ZonedDateTime withXxx(时间) | 修改时间系列的方法 |
ZonedDateTime minusXxx(时间) | 减少时间系列的方法 |
ZonedDateTime plusXxx(时间) | 增加时间系列的方法 |
工具类
Period类
//Period计算时间间隔(年月日)
public static void main(String[] args) {
//此刻年月日
LocalDate today = LocalDate.now();
System.out.println(today);
//昨天年月日
LocalDate yesterday = today.minusDays(1);
System.out.println(yesterday);
//Period对象表示时间的间隔对象
Period period = Period.between(yesterday, today);
System.out.println(period);
//获取两个日期之间相差的年数
System.out.println(period.getYears());
//获取两个日期之间相差的月数
System.out.println(period.getMonths());
//获取两个日期之间相差的日数
System.out.println(period.getDays());
}
/*输出为
2024-04-05
2024-04-04
P1D
0
0
1
*/
Duration类
//Duration计算日期间隔(时分秒)
public static void main(String[] args) {
//此刻时分秒日期对象
LocalDateTime today = LocalDateTime.now();
System.out.println(today);
//昨天的时分秒日期对象
LocalDateTime yesterday = today.minusDays(1);
System.out.println(yesterday);
//Duration对象表示时间的间隔对象
Duration duration = Duration.between(yesterday, today);//第二个减第一个
System.out.println(duration);
//获取两个日期之间相差的天数
System.out.println(duration.toDays());
//获取两个日期之间相差的小时数
System.out.println(duration.toHours());
//获取两个日期之间相差的分钟数
System.out.println(duration.toMinutes());
//获取两个日期之间相差的秒数
System.out.println(duration.getSeconds());
//获取两个日期之间相差的毫秒数
System.out.println(duration.toMillis());
//获取两个日期之间相差的纳秒数
System.out.println(duration.toNanos());
}
/*输出为
2024-04-05T16:31:09.879749900
2024-04-04T16:31:09.879749900
PT24H
1
24
1440
86400
86400000
86400000000000
*/
ChronoUnit类(最重要功能最全)
public static void main(String[] args) {
//此刻时间
LocalDateTime today = LocalDateTime.now();
System.out.println(today);
//其他时间
LocalDateTime otherday = LocalDateTime.of(1920, 4,5,0,0,0,0);
System.out.println(otherday);
System.out.println("相差的年数:"+ ChronoUnit.YEARS.between(otherday,today) );
System.out.println("相差的月数:"+ ChronoUnit.MONTHS.between(otherday,today) );
System.out.println("相差的周数:"+ ChronoUnit.WEEKS.between(otherday,today) );
System.out.println("相差的天数:"+ ChronoUnit.DAYS.between(otherday,today) );
System.out.println("相差的小时数:"+ ChronoUnit.HOURS.between(otherday,today) );
System.out.println("相差的分钟数:"+ ChronoUnit.MINUTES.between(otherday,today) );
System.out.println("相差的秒数:"+ ChronoUnit.SECONDS.between(otherday,today) );
System.out.println("相差的毫秒数:"+ ChronoUnit.MILLIS.between(otherday,today) );
System.out.println("相差的纳秒数:"+ ChronoUnit.NANOS.between(otherday,today) );
System.out.println("相差的半天数:"+ ChronoUnit.HALF_DAYS.between(otherday,today));
System.out.println("相差的十年数:"+ ChronoUnit.DECADES.between(otherday,today));
System.out.println("相差的世纪数:"+ ChronoUnit.CENTURIES.between(otherday,today));
System.out.println("相差的千年数:"+ ChronoUnit.MILLENNIA.between(otherday,today));
System.out.println("相差的纪元数:"+ ChronoUnit.ERAS.between(otherday,today));
}
/*输出为:
2024-04-05T16:47:01.142283900
1920-04-05T00:00
相差的年数:104
相差的月数:1248
相差的周数:5426
相差的天数:37986
相差的小时数:911680
相差的分钟数:54700847
相差的秒数:3282050821
相差的毫秒数:3282050821142
相差的纳秒数:3282050821142283900
相差的半天数:75973
相差的十年数:10
相差的世纪数:1
相差的千年数:0
相差的纪元数:0
*/
二、单列集合
2.1Collection接口
List和set是Collection接口的子接口。
Collection接口的常用方法
方法名称 | 说明 |
---|---|
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
poblic boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中元素的个数/集合的长度 |
//以多态的形式创建集合对象,调用到单列集合中的共有方法
Collection<Sring> c = new ArrayList<>();
集合的通用遍历
迭代器遍历
public class Main {
/*
public Iterator<E> iterator() : 获取遍历集合的迭代器
public E next() : 从集合中获取一个元素
*/
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("张三");
c.add("李四");
c.add("王五");
//获取迭代器
Iterator<String> it = c.iterator();
//循环判断集合中是否还有元素
while (it.hasNext()) {
//调用next方法将元素取出
System.out.println(it.next());
}
}
}
/*
hasNext() :判断集合中是否还有元素
next() : 取出集合元素,并将指针向后移动
注意:在循环过程中next方法最好只调用一次
*/
增强for循环
- 简化迭代器的代码书写
- 在JDK5版本后出现,内部原理就是一个Iterator迭代器
for(String s(变量名) : list(数组或者集合){
System.out.println(s);
}
//打印出所有元素
foreach方法
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("张三");
c.add("李四");
c.add("王五");
c.forEach(new Consumer<String>() { //传入接口对象,使用匿名内部类实现接口
@Override
public void accept(String s) { //重写accept方法
System.out.println(s);
}
});
}
}
//替换为Lambda表达式
c.forEach(s -> System.out.println(s));
2.2List集合
List是一个接口,不能直接new对象,因此可以用ArrayList类等创建对象。
List接口的特点
- 存取有序
- 有索引
- 可以存储重复的
和List有关的方法
方法 | 说明 |
---|---|
public void add(int index,E element) | 在指定索引位置添加元素 |
public E remove(int index) | 根据索引删除集合中的元素 |
public E set(int index,E element) | 根据索引修改集合中的元素 |
public E get(int index) | 返回指定索引处的元素 |
List集合的遍历方式
在通用遍历方式上多了普通for循环和ListIterator(List集合特有的迭代器)
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for(int i =0; i < list.size(); i++)
System.out.println(list.get(i));
//普通for循环
System.out.println("===============================================");
ListIterator<String> iterator = list.listIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
//迭代器正向循环
System.out.println("===============================================");
while (iterator.hasPrevious()){
System.out.println(iterator.previous());
}
//迭代器反向循环
}
/*输出
a
b
c
d
===============================================
a
b
c
d
===============================================
d
c
b
a
*/
**注意:**考虑到cursor指针,只有正向循环后,指针指向集合最后一个元素,才能使用反向循环反向遍历
并发修改异常
即ConcurrentModificationException
使用[迭代器]遍历集合的过程中,调用了[集合对象]的添加方法,删除方法,就会出现此异常。
解决方法:使用迭代器自己的添加删除方法。
ps:使用集合方法删除倒数第二个元素时不会出现错误,删除后不会最后调用next()方法产生异常
2.3ArrayList类
ArrayList原理
ArrayList底层是基于数组实现的,根据查询元素快,增删相对慢
- 创建ArrayList集合容器时,底层会存在一个长度大小为10个大小的空数组
- 原数组满时,扩容原数组1.5倍变成新数组
- 将原数组内容拷贝到新数组中
- 将新元素添加到新数组中
ArrayList集合的使用
构造方法
public ArrayList();//创建一个空的集合容器
public static void main(String[] args) {
ArrayList list = new ArrayList();
//可以存储任何数据
list.add(100);
list.add("100");
list.add(true);
list.add('1');
System.out.println(list);//可以直接输出内容
}
/*输出
[100, 100, true, 1]
*/
对存储数据类型作限制
ArrayList<String> list = new ArrayList<String>;
//只能存储String类型数据
<>:泛型
注意:泛型里不允许编写基础数据类型,要使用基本数据类型的包装类。
ArrayList常用成员方法
方法名 | 说明 |
---|---|
public boolean add (E e) | 将指定的元素添加到此集合的末尾 |
public void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
public E get(int index) | 返回指定索引处的元素 |
public int size() | 返回集合中的元素的个数 |
public E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
public boolean remove(Object o) | 删除指定的元素,返回删除是否成功 |
public E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
2.4LinkedList类
LinkedList底层基于双链表实现,查询元素慢,增删首尾元素非常快
特有方法
方法名 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
2.5泛型
JDK5引入的,可以在编译阶段约束操作的数据类型,并进行检查,可以统一数据类型,将运行期的错误提升到了编译期。
**注意:**泛型默认的类型为Object
泛型标识符
- E : Element
- T : Type
- K : Key(键)
- V : Value(值)
泛型类与泛型方法
在编写类的时候加入泛型标识符,在创建对象时确定具体的类型
非静态的方法内部的泛型会根据类的泛型进行匹配
静态方法中要声明出自己独立的泛型,在调用方法时确定具体类型
public class Main{
public static void main(String[] args) {
Student<String> stu = new Student<>();//此时确定为String类型
}
}
class Student<>{
private E e;
public E getE(){
return e;
}
public void setE(E e){//根据类的泛型进行匹配
this.e = e;
}
public static<T> void print(T[] arry){//独立声明泛型
....
}
}
泛型接口
interface Inter<E>{
void show(E e);
}
class InterAple implements Inter<string>{
public void show(String s){
...//此时确定泛型种类
}
}
class InterBple<E> implements Inter<E>{
//接口泛型与类的泛型相匹配
public void show(E e){
...//变成类泛型的确定时机
}
}
泛型通配符
? :任意类型
? extends E :可以传入的是E或者它的子类
? super E:可以传入E或者它的父类
2.6TreeSet
TreeSet底层由红黑树(这里不再介绍平衡二叉树与红黑树)实现,可以排序与去重。
TreeSet自然排序
- 类实现Comparable接口
- 实现Comparable接口才能制定规则(使用compare方法)进行排序
- 重写compareTo方法
- 根据方法的返回值,来组织排序规则
- 负数:左边走(小于)
- 正数:右边走(大于)
- 0:不存(树根存)(即去重)
public class Student implements Compareble<Student>{
String name;
int age;
@Override//重写Comparable接口中的compareTo方法
public int compareTo(Student o){
return this.age - o.age; //要存储节点减去当前目标节点的年龄
}
}
/*
this.xxx - o.xxx 正序
o.xxx - this.xxx 降序
*/
TreeSet<Student> ts = new TreeSet<>();
ts.add(new Student("王五",25));
ts.add(new Student("张三",23));
ts.add(new Student("李四",24));
ts.add(new Student("赵六",26));
//实现过Comparable接口才能正常排序
TreeSet比较器排序
如果同时具备比较器和自然排序,会有优先挨揍比较器的规则进行排序。
import java.util.*;
public class Main {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override//使用比较器正向排序
public int compare(Student o1, Student o2) {
int ageResult = o1.getAge() - o2.getAge();
return ageResult == 0 ? o1.getName().compareTo(o2.getName()) : ageResult;
}
});
ts.add(new Student("张三",26));
ts.add(new Student("李四",24));
ts.add(new Student("王五",25));
ts.add(new Student("赵六",26));
System.out.println(ts);
}
}
public class Student implements Comparable<Student> {
private String name;
private int age;
@Override
public int compareTo(Student o) {
return o.age-this.age; //使用compareTo年龄方法降向排序
}
public Student(){
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
int getAge() {
return age;
}
String getName(){
return name;
}
public String toString(){
return "Student{name = "+ name + ",age = "+ age + "}";
}
}
/*输出为:
[Student{name = 李四,age = 24}, Student{name = 王五,age = 25}, Student{name = 张三,age = 26}, Student{name = 赵六,age = 26}]
为正向排序
*/
如果要实现的需求,排序规则与已经具备的自然排序不一样,就要使用比较器排序。
2.7HashSet
- HashSet底层采取哈希表储存数据
- 哈希表是一种对于增删改查数据性能都较好的结构
哈希表:
- JDK8之前:数组+链表
- JDK8之后:数组+链表+红黑树
HashSet的去重
HashSet去重需要对象同时重写equals方法和hashCode方法
- 当添加对象的时候,会先调用对象的hashCode方法计算出一个应该存入的索引位置,查看该位置上是否存在元素
- 不存在:直接存储
- 存在:调用equals方法比较内容
- false:(不同)存
- true:(相同)不存
**注意:**重写hashCode方法时,应该将该类的所有属性参与到哈希值的计算中,只有这样做哈希值冲突的概率才会越小,即使哈希值冲突,也会调用equals方法判断两个数据是否相同。
(哈希值不同一定不同,哈希值相同使用equals比较是否相同)
import java.util.*;
public class Main {
public static void main(String[] args) {
HashSet<Student> hs = new HashSet<>();
hs.add(new Student("张三",26));
hs.add(new Student("李四",24));
hs.add(new Student("王五",25));
hs.add(new Student("王五",25));//名字年龄一样,会去重
System.out.println(hs);
}
}
public class Student implements Comparable<Student> {
private String name;
private int age;
@Override
public int compareTo(Student o) {
return o.age-this.age;
}
.................
public String toString(){
return "Student{name = "+ name + ",age = "+ age + "}";
}
//idea自动生成
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);//用Object类中的hash方法计算两个属性的哈希值
}
}
/*输出
[Student{name = 李四,age = 24}, Student{name = 王五,age = 25}, Student{name = 张三,age = 26}]
*/
hashCode方法
哈希值
- 是JDK根据某种规则算出来的int类型的整数
- 调用底层 C++代码计算出来的一个随机数(常被人称作地址值)
哈希表的详细流程
- 创建一个默认长度为16,默认加载因为0.75的数组,数组名table
- 根据元素的哈希值跟数组的长度计算出应存入的位置
- 判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素,则调用equals方法比较属性值,如果一样则不存,如果不一样则存入数组
- 当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍
- 当链表挂载元素超过了8个(阈值)
- 检查数组长度:
- 没有达到64,扩容数组
- 到达64,会转换为红黑树
- 检查数组长度:
2.8LinkedHashSet
- 能去重,并保证存取有序
- 底层数据结构依然是哈希表,只是每个元素又额外的多了一个双链表的机制记录储存的顺序
public static void main(String[] args) {
LinkedHashSet<String> lhs = new LinkedHashSet<String>();
lhs.add("a");
lhs.add("b");
lhs.add("c");
lhs.add("d");
lhs.add("d");
System.out.println(lhs);
}
}
/*输出:
[a, b, c, d]
*/
2.9Collections集合工具类
可变参数
本质上是数组,可以不确定参数数量,在形参中接受多个数据
数据类型后面加上“…"
public static void main(String[] args) {
System.out.println(getSum(1,2,3));
System.out.println(getSum(1,2,3,4,5,6));
}
public static int getSum(int... nums){//本质是int数组
int sum = 0;
for (int num : nums) {//增强for循环遍历
sum += num;
}
return sum;
}
Collections集合工具类
- java.utils.Collections:是集合工具类
- 作用:Collections并不属于集合,是用来操作集合的工具类
方法名称 | 说明 |
---|---|
public static boolean addAl1(Collection<? super T> c,T…elements) | 给集合对象批量添加元素 |
public static void shuffle(List<?> list) | 打乱List集合元素的顺序 |
public static int binarySearch (List list, T key) | 以二分查找法查找元素 |
public static void max/min(Collection coll) | 根据默认的自然排序获取最大/小值 |
public static void swap(List<?> list, int i,int j) | 交换集合中指定位置的元素 |
三、双列集合
双列集合底层数据机构都是针对键有效,跟值没有关系
3.1Map集合
-
Map集合是一种双列集合,每个元素包含两个数据
-
Map集合的每个元素的格式:key = value(键值对应元素)
- key(键):不允许重复
- value(值):允许重复
- 键和值是一一对应的,每个键只能找到自己对应的值
-
key + value这个整体称为“键值对”或者“键值对对象”,在java中使用Entry对象表示
Map集合常用方法
方法名 | 说明 |
---|---|
public V put(K key,V value) | 添加元素(修改:如果键已经存在了,就会使用新值,替换旧值) |
public V get(Object key) | 根据键查找对应的值 |
public Set keySet() | 获取Map集合中所有的键 |
public Set<Map.Enter<K,V>> entrySet() | 获取集合中所有键值对对象 |
public V remove(0bject key) | 根据键删除键值对元素 |
public void clear() | 移除所有的键值对元素 |
public boolean containsKey(0bject key) | 判断集合是否包含指定的键 |
public boolean containsValue(Object value) | 判断集合是否包含指定的值 |
public boolean isEmpty() | 判断集合是否为空 |
public int size() | 集合的长度,也就是集合中键值对的个数 |
default void forEach (BiConsumer<? super K,? super V> action) | 遍历map集合,获取键和值 |
Map是双列集合的顶层接口,它的功能是全部双列集合都可以正常继承使用的
3.2TreeMap、HashMap、LinkedHashMap
除存储按照键值对方式以外,其余存储、排序、去重方式与Set集合操作几乎一样。
3.3Map集合的遍历
由键找值
使用get方法和ketSet方法
import java.util.*;
public class Main {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("张三","上海");
map.put("李四","北京");
map.put("王五","成都");
//获取所有的键
Set<String> keySet = map.keySet();
//遍历set集合,获取每一个键
for (String key : keySet) {
//调用map集合的get方法查找键对应的值
System.out.println(key + "---" + map.get(key));
}
}
}
/*输出:
李四---北京
张三---上海
王五---成都
*/
通过键值对对象来获取键和值
使用entrySet方法和getKey方法和getValue方法
import java.util.*;
public class Main {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("张三","上海");
map.put("李四","北京");
map.put("王五","成都");
//获取所有键值对对象
Set<Map.Entry<String, String>> entrySet = map.entrySet();
//遍历set集合获取每一个键值对对象
for (Map.Entry<String, String> entry : entrySet) {
//通过键值对对象获取键和值
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
}
/*输出:
李四 北京
张三 上海
王五 成都
*/
通过foreach方法
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("张三","上海");
map.put("李四","北京");
map.put("王五","成都");
map.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String key, String value) {
System.out.println(key + "---" + value);
}
});
//Lambda表达式形式:map.forEach((key, value) -> System.out.println(key + "---" + value));
}