Java第三周预习-API、集合

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 SetString> 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底层是基于数组实现的,根据查询元素快,增删相对慢

  1. 创建ArrayList集合容器时,底层会存在一个长度大小为10个大小的空数组
  2. 原数组满时,扩容原数组1.5倍变成新数组
  3. 将原数组内容拷贝到新数组中
  4. 将新元素添加到新数组中
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自然排序
  1. 类实现Comparable接口
    • 实现Comparable接口才能制定规则(使用compare方法)进行排序
  2. 重写compareTo方法
  3. 根据方法的返回值,来组织排序规则
    • 负数:左边走(小于)
    • 正数:右边走(大于)
    • 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++代码计算出来的一个随机数(常被人称作地址值)
哈希表的详细流程
  1. 创建一个默认长度为16,默认加载因为0.75的数组,数组名table
  2. 根据元素的哈希值跟数组的长度计算出应存入的位置
  3. 判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素,则调用equals方法比较属性值,如果一样则不存,如果不一样则存入数组
  4. 当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍
  5. 当链表挂载元素超过了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));
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值