1.object类
1.概述
java.lang.object类是java语言中的根类,即所有类的超类(基类)他描述的所有的方法子类都可以使用,在对象实例化的时候最终找到的类就是object
如果一个类没有特别指定父类,那么默认则继承自object类例如
public class Test01 /*extends object*/ {}
object 类当中包含方法有11个
- public String tostring(); 返回该对象的字符串表示
- public boolean equals(Object obj):指示其他某个实例,是否与此实例相等
2.toString方法
1.摘要
介绍:
toString方法返回该对象的字符串表示, 其实该字符串内容就是对象的类型+@+内存地址值.
由于toString方法返回的结果是内存地址, 我们经常按照对象的属性得到相应的字符串表现形式, 因此我们需要重写他.
2.覆盖重写
如果不希望使用toString方法的默认行为则可以对它进行重写 , 例:
public class Person {
String name;
int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
3.equals方法
1.摘要
equals() 方法, 判断其他某个对象是否和子对象相等.
调用成员方法equals并指定参数为另一个对象,则可以判断这两个对象是否是相同的
- 默认地址比较
如果没有覆盖重写equals 方法, 那么Object类中默认进行== 运算符的对象进行比较, 只要不是同一个对象结果必为false.
- 对象内容比较
如果希望进行对象的内容比较, 所有或者指定的部分成员变量相同就判定两个对象相同, 则可以重写equals方法
public class Person {
String name;
int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
}
4.Object类
在jdk7 中添加了一个Object工具类, 他提供了一些方法来操作对象, 他由一些静态的实例方法组成, 这些方法是null-save(空指针安全的)或者null-tolerant(容忍空指针的),用于计算对象的hashcode,返回对象的字符串表示形式, 计较两个对象.
-
public static boolean equals(Object o , Object b ) 判断两个对象是否相等,
-
源码
public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); }
2.常用类
1.System类
java.lang.System类中提供了大量的静态方法,可以获取与系统香瓜你的信息或系统级操作, 在System类的API文档中常用的方法有:
- public static long currentTimeMillis():返回以毫秒为单位的当前时间.
- public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length):将数组中指定的数据拷贝到另一个数组中
1.1,currentTimeMillis 方法
currentTimeMillis 方法就是获取当前系统时间与1970年01月01日00:00点之间的毫秒差值
public static void main(String[] args) {
long l = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
System.out.println(i);
}
long l1 = System.currentTimeMillis();
System.out.println("耗时:"+(l1-l)); //耗时:256 毫秒
}
1.2.arraycopy方法
将数组中的指定数据拷贝到另一个数组中,
数组的拷贝动作是系统级的, 性能很高,其中有五个参数:
参数名称 参数类型 参数含义
src Object 原数组
srcPos int 原数组索引其起始位置
dest Object 目标数组
destPos int 目标数组索引起始位置
length int 复制元素的个数
public static void main(String[] args) {
int[] arr01 = {1,2,3,4,5};
int[] arr02 = {6,7,8,9,10};
System.arraycopy(arr01,0,arr02,0,2);//将arr01的索引为0出开始拷贝到arr02从索引为0出粘贴, 拷贝出的数量为2
System.out.println(Arrays.toString(arr02));//[1, 2, 8, 9, 10]
}
2.StringBuilder类
2.1 字符串拼接问题
由于strign类的对象内容是不可变的,所以每当进行字符串拼接时,总是会在内存中创建一个新的对象:例如
public static void main(String[] args) {
String str01 = "Hello";
System.out.println(str01.hashCode());//69609650
str01 += "world";
System.out.println(str01.hashCode());//468881952
System.out.println(str01);//Helloworld
}
在api中对string类的描述是:java中所有的自付出那蚊子(例如:“abc”)都被时限为此实例.字符后才能不变,他们的值在创建后不能被更改,字符串缓冲区支持可变字符串, 因为String对象是不可能变得 ,他们可以被共享.
上面代码可以用下图表示:
由图片可知, 如果对字符串进行拼接操作,每次拼接都会在内存中开辟空间,创建一个新的string对象, 既耗时又消耗空间,为了解决这种问题, 可以使用java.lang.StringBuilder类
2.2 StringBuilder
javaAPI:StringBulider又称为可变字符序列, 他是一个类似String的字符串缓冲区, 通过某些方法调用可以改变该序列的长度和内容,
StringBuilder相当于一个容器, 可以装很多的字符串, 并且能够对字符串进行相应的操作,它的内部拥有一个数组来存放字符字符串内容, 进行字符串拼接时, 直接在数组中插入新的内容,StringBuilder会自动维护数组的扩容,
2.2.1构造方法
常用的构造方法有两个:
- public StringBuilder():构造一个空的StirngBuilder容器.
- public StringBulider(String str):构造一个空的StirngBuilder容器,并将字符串添加进去
2.2.2 常用方法
StringBuilder常用方法有两个:
-
public StringBuilder append():添加任意类型数据的字符串形式,并返回当前对象自身.
-
public StringBulider toString(): 将当前StringBuilder 对象转换为String对象.
public static void main(String[] args) {
//创建对象
StringBuilder stringBuilder = new StringBuilder();
//添加任意类型数据
stringBuilder.append(“hello”);
System.out.println(stringBuilder.hashCode());//1163157884
stringBuilder.append(“world”);
System.out.println(stringBuilder.hashCode());//1163157884
stringBuilder.append(1);
stringBuilder.append(true);
stringBuilder.append(1.1f);
System.out.println(stringBuilder.toString());
//上面写法可以写成
System.out.println(stringBuilder.append(“hello”).append(“world”).append(1).append(true).append(1.1f).toString());
}
3.包装类
3.1 概述
java的数据类型基本有两种, 基本类型与引用类型, 基本类型的话效率比较高 , 但是我们一般会使用引用类型, 这样我们创建对象可以进行更多的操作, 我们也可以把基本类型转换成引用类型, 就可以使用基本类型的包装类;
基本类型 对应包装类 java.lang包中
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
3.2 拆箱与装箱
基本类型与引用类型之间转换的过程,我们称之为"装箱"与"拆箱"
- 装箱 :从基本类型转换为引用类型
- 拆箱 :从引用类型转换为基本类型.
基本类型->包装类;
Integer i = new Integer(4);//使用构造器方法
Integer i1 = Integer.valueOf(4);//使用包装类的valueOf() 方法
包装类->基本类型
int i2 = i.intvalue();
因为我们平时经常基本类型与包装类之间转换 ,从 jkd1.5开始, 基本类型与包装类之间不用手动进行拆装箱, 可以自动完成
Integer i = 8 ;
int a = i + 8;
4.基本类型与字符转之间的转换
4.1 基本类型转换为引用类型;
有三种方式:
- 基本类型直接+"" 例如: 3+"";
- 调用类的串转换方法:X.toString();
- 使用String的方法:String.valueOf(X);
4.2 String 类型转换为基本类型
除了Character 类之外, 其他所有的包装类都具有parseXxx 静态方法可以将字符串转换为基本数据类型;
- public static byte parseByte(String s ) :将字符串转换为对应的byte类型
- public static short parseShort(String s ):将字符串转换为对应的 shord 类型
- public static int parseInt(String s ):将字符串转换为对应的int 类型
- public static long parseLong(String s ):将字符串转换为对应的 long 类型
- public static float parseFloat(Stirng s):将字符串转换为对应的 float 类型
- public static double parseDouble (String s);将字符串转换为对应的 double 类型
- public static boolean parseDoolean (String s ):将字符串转换为对应的 boolean 类型
//举个例子:
public static void main(String[] args) {
String s = “true00000”;
boolean b = Boolean.parseBoolean(s);
System.out.println(b);//false
if (b) {
System.out.println(“222”);
}
String s1 = “12324”;
int i = Integer.parseInt(s1);
System.out.println(i);//12324
}
如果无法正确转换 会抛出
java.lang.NumberFormatException.异常
5.日期时间类
5.1 Date类
5.1.1 概述
java.util.Date 类 表示指定的瞬间,精确到毫秒
jdk文档 介绍:一个大约一毫秒值的薄包装,允许JDBC将其标识为SQL DATE值。 毫秒值表示1970年1月1日00:00:00.000 GMT之后的毫秒数。
为了符合SQL DATE ,由java.sql.Date实例包装的毫秒值必须通过在实例关联的特定时区中将小时,分钟,秒和毫秒设置为零来“归一化”。
构造方法 : 例
Date(int year, int month, int day) 已弃用 而是使用构造Date(long date)
Date(long date) 使用给定的毫秒时间值构造一个 Date对象。
Date 拥有多个构造方法, 只是大部分已经过时, 但是其中有未过时的函数可以吧毫秒值转换成日期
- public Date():分配Date对象并初始化此对象, 以表示分配他的时间(精确到毫秒)
- public Date(long date):分配Date对象并初始化此对象, 以表示自从标准时间(成为"历元(epoch)", 即使1970年01月01日00:00:00 GMT) 以来的毫秒数.
tips: 由于中国处于东八区(GMT+08:00)是比世界协调时间/格林尼治时间(GMT)快8小时的时区,当格林尼治标准时间为0:00时,东八区的标准时间为08:00。
使用无参构造器,可以自动设置当前系统时间的毫秒时刻; 指定long类型的构造参数, 可以定义毫秒时刻,例如:
public static void main(String[] args) {
System.out.println(new Date());//Mon Jun 29 16:06:19 CST 2020
System.out.println(new Date(0L));//Thu Jan 01 08:00:00 CST 1970
}//在使用println方法时,会自动调用Date类中的toString方法。Date类对Object类中的toString方法进行
//了覆盖重写,所以结果为指定格式的字符串。
5.1.2 常用方法
Date类中有很多方法已经过时, 常用的方法有:
- public long getTime(): 把日期对象转换成对象的时间毫秒值
5.2 DateFormat类
java.text.DateFormat: 是日期/时间格式化子类的抽象类,我们通过这个类可以帮我们完成日期和文本之间的转换,也就是可以在Date对象与String对象之间进行来回转换。
- 格式化: 按照指定的格式, 从Date对象转换为String对象.
- 解析: 按照指定的格式,从String 转换成Date对象.
5.2.1 构造方法
由于DataFormat 为抽象类 , 不能被实例化, 所以我们经常使用他的子类,java.text.SimpleDateFormat:这个类需要一个格式来指定格式化或解析的标准, 构造方法为:
- public SimpleDateFormat(String pattern):用于给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat.参数patterm 是一个字符串, 代表日期时间的定义格式;
5.2.2 格式规则
表示字母(区分大小写) 含义 例子
Y
y 年 1997;07
M 月 7月;8月
m 分
D
d 日
H 时
h
S
s 秒
W
w
K
k
Z
z
F
E
G 文本 AD
X
L
a
u
具体介绍参考JDK文档
5.2.3 常用方法
DateFormat类的常用方法有:
-
public String format(Date date):将Date对象格式化为字符串;
-
public Date parse(String source):将字符串转换为Date 对象;
public static void main(String[] args) throws ParseException {
//将当前日期转换为字符串
SimpleDateFormat sl = new SimpleDateFormat(“yyyy-MM-dd”);
String format = sl.format(new Date());
System.out.println(format);
//将当前时间转换为日期类型
SimpleDateFormat sl1 = new SimpleDateFormat(“yyyyMMddhhmmss");11
Date parse = sl1.parse("199730231032”);
//将日期类型转化为Stirng
String format1 = sl1.format(parse);
System.out.println(format1);
}
5.3Calender类
5.3.1概念
java.util.Calendar 是日历类, 在Date之后出现, 替换掉许多Date的方法, 该类将所有可能用到的时间信息封装为静态成员变量,日历类就是方便获取各个时间属性的.
5.3.2 获取方式
Calendar 为抽象类, 不能直接创建, 而是通过静态方法创建,返回子类对象
Calendar 静态方法
-
public static Calendar getInstance():使用默认失去和语言环境获得一个日历
Calendar calendar = Calendar.getInstance();
5.3.3 常用方法
常用的方法有:
- public int get(int field):返回给定日期字段的值;
- public void set (int field , int value):将制定的日期字段设置为给定值.
- public abstract void add(int field , int amount):根据日历的规则, 为给定的日历字段添加或减去指定的时间量.
- public Date getTime():返回一个标识此Calendar 时间值(从历元到现在的毫秒的偏移量)的Date对象.
Calendar类中提供很多成员常量, 代表给定的日期字段:
常用:
字段值 含义
YEAR 年
MONTH 月(从0开始,可以+1使用)
DAY_OF_MONTH 用中的第几天(几号)
HOUR 时(12小时)
HOUR_OF_DAY 时(24小时)
MINUTE 分
SECOND 秒
DAY_OF_WEEK 周中的天(周几,周日为1, 可以用-1使用)
JDK提供:
Modifier and Type Field and Description
static int ALL_STYLES getDisplayNames的样式说明符, 表示所有样式的名称,如“1月”和“1月”。
static int AM AM_PM字段的值表示从午夜到中午之前的一天中的一段时间。
static int AM_PM 对于现场数 get和 set指示是否 HOUR是前或中午之后。
static int APRIL MONTH字段的价值 指示了格里高利和朱利安日历中的第四个月。
protected boolean areFieldsSet 如果 fields[]与当前设置的时间同步,则为真。
static int AUGUST MONTH领域的价值 指示了公历和朱利安日历中的第八个月。
static int DATE get和 set字段编号表示该月的日期。
static int DAY_OF_MONTH get字段编号和 set本月的日期。
static int DAY_OF_WEEK get字段编号和 set表示一周中的日期。
static int DAY_OF_WEEK_IN_MONTH get字段编号和 set当月的 set几的序号。
static int DAY_OF_YEAR get和 set字段编号, set本年度的日数。
static int DECEMBER MONTH字段的值表示公历和朱利安日历中的第十二个月。
static int DST_OFFSET get和 set字段编号 get夏令时偏移量(以毫秒为单位)。
static int ERA get和 set字段号表示时代,例如在儒略历中的AD或BC。
static int FEBRUARY MONTH字段的价值表示今年第二个月在公历和朱利安日历。
static int FIELD_COUNT get和 set的不同字段的数量。
protected int[] fields 该日历的当前设置时间的日历字段值。
static int FRIDAY DAY_OF_WEEK字段的值表示周五。
static int HOUR get和 set字段编号, get上午或下午的小时。
static int HOUR_OF_DAY get字段编号和 set当天的小时数。
protected boolean[] isSet 说明是否设置日历的指定日历字段的标志。
protected boolean isTimeSet 如果那么那么 time的值是有效的。
static int JANUARY MONTH字段的价值表示今年首次在公历和朱利安日历。
static int JULY MONTH字段的值代表了 公历和朱利安日历中的第七个月。
static int JUNE MONTH字段的价值 指示了公历和朱利安日历中的第六个月。
static int LONG getDisplayName和 getDisplayNames相当于 LONG_FORMAT的样式说明 符 。
static int LONG_FORMAT getDisplayName和 getDisplayNames的样式说明 符 , 表示用于格式的长名称。
static int LONG_STANDALONE 一个 getDisplayName和 getDisplayNames的样式说明 符 , 表示一个独立使用的长名称,例如月份名称作为日历头。
static int MARCH MONTH字段的值代表了 公历和朱利安日历中的第三个月。
static int MAY MONTH领域的价值 指示了公历和朱利安日历中的第五个月。
static int MILLISECOND get和 set字段号表示 get内的 set数。
static int MINUTE get和 set字段编号表示小时内的分钟。
static int MONDAY DAY_OF_WEEK字段的值表示星期一。
static int MONTH get和 set字段号表示月份。
static int NARROW_FORMAT getDisplayName和 getDisplayNames的样式说明 符 , 表示用于格式的窄名称。
static int NARROW_STANDALONE getDisplayName和 getDisplayNames的样式说明 符 独立地表示一个狭义的名称。
static int NOVEMBER MONTH领域的价值 指示了公历和朱利安日历中的第十一个月。
static int OCTOBER MONTH字段的价值表示在公历和朱利安日历中的一年中的第十个月。
static int PM AM_PM字段的值表示从中午到午夜之前的一天中的一段时间。
static int SATURDAY DAY_OF_WEEK字段的值表示星期六。
static int SECOND get和 set字段编号表示分钟内的第二个。
static int SEPTEMBER MONTH字段的值代表了 公历和朱利安日历中的第九个月。
static int SHORT getDisplayName和 getDisplayNames的样式说明 符 , 相当于 SHORT_FORMAT 。
static int SHORT_FORMAT getDisplayName和 getDisplayNames的样式说明 符 , 表示用于格式的短名称。
static int SHORT_STANDALONE 一个用于 getDisplayName和 getDisplayNames的样式说明 符 , 表示一个简单的名称,例如一个月缩写作为日历头。
static int SUNDAY DAY_OF_WEEK字段的值表示星期天。
static int THURSDAY DAY_OF_WEEK字段的值表示星期四。
protected long time 这个日历的当前设定时间,以1970年1月1日,格林尼治标准时间0:00:00之后的毫秒表示。
static int TUESDAY DAY_OF_WEEK字段的值表示周二。
static int UNDECIMBER MONTH字段的值表示一年的第十三个月。
static int WEDNESDAY DAY_OF_WEEK字段的值表示周三。
static int WEEK_OF_MONTH get和 set字段编号, set当月的周数。
static int WEEK_OF_YEAR get和 set字段编号, set本年度的周数。
static int YEAR get现场编号和 set表示年份。
static int ZONE_OFFSET get和 set字段编号, get GMT以毫秒为 get的原始偏移量。
public static void main(String[] args) {
//获取Calendar对象
Calendar cal = Calendar.getInstance();
//获取年
int i = cal.get(Calendar.YEAR);
System.out.println(i);//2020
//设置年
cal.set(Calendar.YEAR,2023);
int i1 = cal.get(Calendar.YEAR);
System.out.println(i1);//2023
//将年份修改为2003年
cal.add(Calendar.YEAR,-20);
int i2 = cal.get(Calendar.YEAR);
System.out.println(i2);//2003
SimpleDateFormat sl = new SimpleDateFormat("yyyy-MM-dd");
String format = sl.format(cal.getTime());
System.out.println(format);//2003-06-29
}
西方星期的开始为周日,中国为周一。
在Calendar类中,月份的表示是以0-11代表1-12月。
日期是有大小关系的,时间靠后,时间越大。
6.BigDecimal类
6.1BigDecimal 类概述
java.math.BigDecimal类: 他可以标识一个不可变的, 任意精度的有符号10进制数,同时他也提供了对这种数据运算的一些方法, 以及各种舍入模式, 它尤其可以避免基本数据类型进行浮点运算时损失精度的问题,
public static void main(String[] args) {
float f1 = 0.1f;
double f2 = 0.05;
System.out.println(f1+f2);//0.1500000014901161
}
6.2 BigDecimal 使用
- 构造方法:
- public BigDecimal(Double d):将double 转换成BigDecimal [不建议]
- public BigDecimal(String s):将String 转换为BigDecimal [建议]
- BigDecimal(BigInteger val) 将 BigInteger转换成 BigDecimal 。
BigDecimal(BigInteger unscaledVal, int scale) 将BigInteger的 BigInteger值和 int等级转换为 BigDecimal 。
BigDecimal(BigInteger unscaledVal, int scale, MathContext mc) 将 BigInteger未缩放值和 int扩展转换为 BigDecimal ,根据上下文设置进行舍入。
BigDecimal(BigInteger val, MathContext mc) 根据上下文设置将 BigInteger转换为 BigDecimal舍入。
BigDecimal(char[] in) 一个转换的字符数组表示 BigDecimal成 BigDecimal ,接受字符作为的相同序列 BigDecimal(String)构造。
BigDecimal(char[] in, int offset, int len) 一个转换的字符数组表示 BigDecimal成 BigDecimal ,接受字符作为的相同序列 BigDecimal(String)构造,同时允许一个子阵列被指定。
BigDecimal(char[] in, int offset, int len, MathContext mc) 一个转换的字符数组表示 BigDecimal成 BigDecimal ,接受字符作为的相同序列 BigDecimal(String)构造,同时允许指定一个子阵列和用根据上下文设置进行舍入。
BigDecimal(char[] in, MathContext mc) 一个转换的字符数组表示 BigDecimal成 BigDecimal ,接受相同的字符序列作为 BigDecimal(String)构造与根据上下文设置进行舍入。
BigDecimal(double val) 将 double转换为 BigDecimal ,这是 double的二进制浮点值的精确十进制表示。
BigDecimal(double val, MathContext mc) 将 double转换为 BigDecimal ,根据上下文设置进行舍入。
BigDecimal(int val) 将 int成 BigDecimal 。
BigDecimal(int val, MathContext mc) 将 int转换为 BigDecimal ,根据上下文设置进行舍入。
BigDecimal(long val) 将 long成 BigDecimal 。
BigDecimal(long val, MathContext mc) 将 long转换为 BigDecimal ,根据上下文设置进行舍入。
BigDecimal(String val) 将BigDecimal的字符串表示 BigDecimal转换为 BigDecimal 。
BigDecimal(String val, MathContext mc) 一个转换的字符串表示 BigDecimal成 BigDecimal ,接受相同的字符串作为 BigDecimal(String)构造,利用根据上下文设置进行舍入。
- 常用方法:
- public BigDecimal add(BigDecimal augend):与参数相加.
- public BigDecimal subract(BigDecimal subtranhend):与参数相减;
- public BigDecimal multiply(BigDecimal multiplicand):与参数相乘
- public BigDecimal divide(BigDecimal divisor): 与参数做除法, 除不尽会抛出异常
- `public BigDecimal divide(BigDecimal divisor, int scale ,int roundingMode):与参数做除法;
-
参数1:除数;
-
参数2:小数点后保留的位数;
-
参数2:舍入模式;
public static void main(String[] args) {
BigDecimal bd1 = new BigDecimal(“1000”);
BigDecimal bd2= new BigDecimal(“3”);
System.out.println(bd1.add(bd2));//加法
System.out.println(bd1.subtract(bd2));//减法
System.out.println(bd1.multiply(bd2));//乘法
//bd3/bd4 保留五位小数,2舍 3入
BigDecimal divide = bd1.divide(bd2, 5, 2);
System.out.println(divide);
}
-
3.常见数据结构
3.1数据结构介绍
数据结构的含义即是 : 数据用什么样的方式排列在一起
3.2常见数据结构
常用的数据结构分为: 栈, 队列, 数组, 链表, 红黑树, 其余不一一列举
3.2.1 栈
- 栈(stack): 又称为堆栈 ,他是运算受限的线性代表, 其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加,查找, 删除等操作.
采用该结构的集合, 对元素的存取有一下特点
- 先进后出: 存进去的元素,要在他后面的元素依次取出后, 才能取出该元素.就好比子弹夹,往里面压入子弹, 射击的时候最上面的先击发;
- 栈的入口和出口都是在栈的最顶端位置
- 压栈 : 就是存入元素, 即把元素存储到栈的顶端位置, 栈中已有元素依次向栈底移动一个位置.
- 弹栈: 就是取出元素,即把栈的顶端位置元素取出, 栈中已有元素依次向栈的顶端移动一个位置.
3.2.2 队列
- 队列(queue) ,简称队, 他从堆栈一样, 也是一种运算受限的线性表, 其限制是仅允许在表的一端进行插入, 而在表的另一端进行删除.
采用该结构的集合,对元素的存取有如下特点:
- 先进先出:存进去的元素,要在他的前面的元素依次取出后, 他能求出该元素; 比如显示生活中的隧道;
- 队列的入口出口,各占一半,
3.2.3 数组
- 数组(array) 是有序的元素序列, 数组是在内存中开辟一端连续的空间, 并在此空间存放元素; 酒店,每个房间都有房间号;可以根据房间号查看每个房间是否有客人;
采用该结构的集合,对元素有一下特点
- 查找元素快: 通过索引,可以快速访问指定位置的元素
- 增删元素比较慢 :
-
指定索引位置增加元素: 需要创建一个新数组,将指定新元素存储在指定索引位置 , 再把原数组根据索引,复制到新数组对应的索引位置;
-
指定索引位置删除元素: 需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置, 原数组中指定索引位置元素不复制到新数组,
-
3.2.4 链表
- 链表(linked list) , 由一些列节点node(链表中每一个元素成为一个节点)组成, 节点可以在运行时动态生成,每个节点包括两部分:一个时存储数据元素的数据域, 另一个是存储下一个节点地址的指针域.链表结构有单向链表和双向链表,
- 单向链表
采用该结构的集合,对元素的存储有一下特点:
- 多个节点之间,通过地址进行连接, 例如自行车链条
- 增删元素比较快
3.2.5 红黑树
- 二叉树(binary tree) , 是每个节点不超过2的有序树(tree).
就是类似于我们现实中的数目结构, 只不过每个树枝最多有两个叉, 顶上的叫根节点, 两边被称为"左子树" 和"右子树"
红黑树本质就是一颗二叉查找树,将节点插入后, 该树仍然是一颗二叉查找树
红黑树可以通过红色节点和黑色节点尽可能的保证二叉树的平衡, 从而提高效率, 查找速度恐怖!
具体什么是红黑树, 是很大一门学问, 需要另起文章,一一讲解;
4.Collection-集合框架
- 集合的概述:
- 集合: 集合使java中提供的一种容器, 可以用来存储多个数据.
- 集合与数组的区别:
- 数组长度是固定的, 集合的长度是可变的.
- 数组中存储的是同一种元素, 可以存储任意类型的数据, 集合中存储的都是引用数据类型,如果想存储基本数据类型需要存储对应的包装类
- 集合的继承体系
Collection:单列集合的根接口,用于存储一系列符合某种规则的元素, 他有两个重要的子接口, 分别是java.util.List和java.util.Set其中, List的特点是有序,元素可重复, Set的特点是元素不可重复.List接口的主要实现类有java.util.ArrayList和java.util.LinkedList,Set接口的主要实现类有java.util.HashSet和’LinedHashSet.
上面这张图包括我们经常使用到的集合框,并不是说仅仅只有这些
集合本身就是一个工具, 它存放在java.util包中,在Collection接口中定义了单例集合框架中共性的部分
- Collection 常用的功能;
Collection ,是所有单列集合的父接口, 因此在Collection 中定义了单列集合(List/Set)通用的一些方法, 这些方法可以用于操作所有的单列集合- 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: 把集合中的元素,存储到数组中
常用的方法基本就以上这么多,还有很多方法可以查看java API 文档
Iterator 迭代器,增强for
- Iterator 接口
在程序开发中,经常需要遍历集合中所有元素, 针对这种需求,JDK专门提供了一个接口
java.util.Iterator
想要遍历Collection集合, 那么就要获取该集合迭代器完成迭代操作,- pubic Iterator iterator():获取集合对应的迭代器, 用来遍历集合中的元素的
- 迭代 : 即Collection 集合元素通用的获取方式, 在取元素之前要先判断集合里面有没有元素, 如果有则取出来,继续判断有的话再取出, 直到把集合中元素全部取出来, 这种方式专业术语叫做迭代
- Iterator 接口的常用方法如下:
-
public E next() :返回迭代的下一个元素.
-
public boolean hasNext(): 如果有下一个元素可以迭代, 返回true.
public static void main(String[] args) {
ArrayList arr = new ArrayList<>();
arr.add(“test01”);
arr.add(“test02”);
arr.add(“test03”);
arr.add(“test04”);
//获取集合的迭代器
Iterator iterator = arr.iterator();
//判断是否有下一个元素
while (iterator.hasNext()) {
String next = iterator.next();
System.out.print(next+"\t");//test01 test02 test03 test04
}
}
-
PS:
-
在进行集合元素获取时, 如果集合中已经没有元素了,还继续调用迭代器的next() 方法, 将会抛出异常:java.util.NoSuchElementException 没有元素异常.
-
在进行集合元素获取时, 如果天剑或移除集合中的元素, 将无法继续迭代,将会抛出
ConcurrentModificationException并发修改异常
-
迭代器的实现原理
当遍历集合时,首先通过调用集合的iterator()方法获取迭代器对象, 然后使用hasNest()方法判断集合中是否有下一个元素,如果存在, 则调用next()方法取出,否则证明已经全部取出, 停止循环遍历
Iterator 迭代器对象在遍历集合时, 内部采用指针的方式来跟踪集合中的元素;在调用iterator 的next()方法之前,迭代器的索引位于第一个元素之前, 不指向任何元素, 当第一次调用迭代器的next()方法之后,迭代器的索引为向后移动一位, 指向第一个元素并返回, 以此类推,知道hasNext()方法返回false; 终止遍历
-
增强for
也称为foreach 循环, 是jkd5之后, 出现的一个高级for循环 , 主要用来遍历元素,内部其实是一个Iterator迭代器, 所以在遍历的时候不能对元素进行增删操作;
格式:
for(元素类型 变量名 : 集合或者数组)
{}
它用于遍历Collection 和数组, 通常只进行遍历,不要在遍历过程中对集合中的元素进行增删操作;
public static void main(String[] args) {
int[] arr = {1, 3, 5, 7, 8};
for (int a : arr) {
System.out.print(a+"\t");//1 3 5 7 8
}
System.out.println();
ArrayList arrayList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
arrayList.add(i + “”);
}
for (String s : arrayList) {
System.out.print(s+"\t");
//0 1 2 3 4 5 6 7 8 9
}
}
PS:
foreach 循环必须有被遍历的目标, 目标只能是Collection 或者数组;
仅仅可以遍历不能增删元素否则会抛出ConcurrentModifcationException并发异常
4.1 List
4.1.1 List介绍
java.util.List接口实现Collection接口, 是单列结合的一个重要分支, 一般将实现List接口的对象成为List集合, 在List集合中允许出现重复的元素, 所有的元素是以一种线性方式进行存储的, 在程序中可以通过索引来访问集合中指定的元素, List集合是有序的, 即元素的存入顺序和取出顺序一致
List接口特点:
- 他是一个元素存取有序的集合, 例如,存入的是 1,2,3 那么集合中元素的存储顺序就是1,2,3
-
他是一个带有索引的集合, 通过索引就可以精确的操作集合中的元素(与数组的索引一样).
-
集合中可以有重复的元素, 通过元素的equals 方法, 来比较是否为重复元素.
4.1.2 List 接口中常用方法
List作为Collection 子接口, 不但继承了Collection 接口中的全部方法, 而且还增加了一些根据元素索引来操作集合的特有方法:
- public void add(int index ,E element):将指定的元素,添加到该集合中的指定位置上
- public E get(int index):返回集合中指定位置的元素.
- public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素.
- pubic E set(int index ,E element): 用指定的元素替换集合中指定位置的元素, 返回替换之前的元素
4.1.3 ArrayList 集合
java.util.ArrayList集合数据的存储结构是数组结构, 元素增删慢, 查找快, 因为日常操作都是查询数据,遍历数据,所以ArrayList 经常使用;
4.1.4 LinkedList 集合
java.util.LinkedList 集合数据存储结构是链表结构,方便元素添加,删除的集合;
LinkList 是一个双向链表结构
LindedList 提供了大量首尾操作的方法:
- public void addFirst(E e): 将指定的元素插入此列表的开头
- public void addLast(E e):将指定的元素插入此列表的结尾
- public E getFirst():返回此列表开头第一个的元素
- public E getLast():返回此列表结尾最后一个元素
- public E removeFirst():移除并返回此列表的第一个元素
- public E removeLast(): 移除并返回此列表的最后一个元素
- public E pop():从此列表锁表示的堆栈处弹出一个元素
- public void push(E e):将元素推入此列表所表示的堆栈.
- public boolean isEmpty():如果列表不包含元素则返回true.
LinkedList是List的子类, List中的方法LinkedList 中都可以使用 , LinkedList 可以被当做堆栈, 队列来使用;
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("str01");
linkedList.add("str02");
linkedList.add("str03");
linkedList.add("str04");
System.out.println(linkedList.toString());
String first = linkedList.getFirst();
System.out.println(first);
String last = linkedList.getLast();
System.out.println(last);
String s = linkedList.removeFirst();
System.out.println(s);
String s1 = linkedList.removeLast();
System.out.println(s1);
System.out.println(linkedList.toString());
linkedList.addFirst("str01");
linkedList.addLast("str04");
System.out.println("========================");
System.out.println(linkedList.toString());
boolean empty = linkedList.isEmpty();
System.out.println(empty);
System.out.println("========================");
String pop = linkedList.pop();
System.out.println(pop);
System.out.println(linkedList.toString());
linkedList.push("test999");
System.out.println(linkedList.toString());
/*
[str01, str02, str03, str04]
str01
str04
str01
str04
[str02, str03]
========================
[str01, str02, str03, str04]
false
========================
str01
[str02, str03, str04]
[test999, str02, str03, str04]
*/
}
4.2 Set
java.util.Set接口和java.util.List接口一样都是Collection的子接口与, 他与Collection 接口中的方法基本一致, 并没有对Collection 接口进行功能上的扩充 , 只是比Collection接口更加的严格, 与List接口不同的是, Set 接口中的元素是无序的, 并且都会以某种规则保证存入的元素不出现重复;Set集合有多个子类, 主要的两个为java.util.hashSet,java.util.LinkedHashSet这两个集合,
取出元素的方式可以采用: 迭代器, 增强for
4.2.1HashSet集合
java.util.HashSet 是Set接口的一个实现类, 他所存储的元素是不可重复的, 并且元素都是无序的(存取顺序无法保证)java.util.HashSet 底层是一个java.util.HashMap 支持,
HashSet 是根据对象的哈希值来确定元素在集合中的存储位置, 因此查找性能比较好, 保证元素唯一性的方式采用 hashcode 和equals方法
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("str1");
hashSet.add("str1");
hashSet.add(new String("str1"));
for (String s : hashSet) {
System.out.println(s);
}
/*
* str1
* str11
* */
}//不同对象, 数据相同也不会储存
4.2.2 HashSet集合存储数据的结构(哈希表)
哈希表是什么?
在jkd1.8之前, 哈希表底层采用数组+链表来实现, 即使用数组处理冲突, 同一hash 值的链表都存储在一个数组里, 但是当位于一个桶中的元素较多, 即hash值相同的元素较多是,通过key 值依次查找效率比较低,在jdk1.8时哈希表存储数据采用数组+链表+红黑树实现,当链表长度超过阈值(8)时, 将链表转换为红黑树,来降低查找时间.
HashSet唯一性通过hashCode和equals 方法来决定的, 如果往集合中添加自定义对象, 要保证其唯一性,就必须重写hashCode和equals 方法简历属于当前对象的比较方式
4.2.3 HashSet 存储自定义类型元素
public class Student {
String name ;
String sex ;
public Student(String name, String sex) {
this.name = name;
this.sex = sex;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) &&
Objects.equals(sex, student.sex);
}
@Override
public int hashCode() {
return Objects.hash(name, sex);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
public class Test_Student {
public static void main(String[] args) {
HashSet<Student> hashSet = new HashSet<>();
hashSet.add(new Student("小红", "23"));
hashSet.add(new Student("小红", "23"));
hashSet.add(new Student("小红", "23"));
hashSet.add(new Student("小红", "23"));
for (Student student1 : hashSet) {
System.out.println(student1);
//Student{name='小红', sex='23'}
}
}
}
4.2.4 LinkedHashSet
HashSet保证元素的唯一性, 但是无法保证放入元素的顺序, 如果要保证顺序的话,在HashSet下面有一个子接口java.util.LinkedHashSet , 他是链表和哈希表组合的一种数据存储结构.
public static void main(String[] args) {
LinkedHashSet<String> set = new LinkedHashSet<String>();
set.add("aaa");
set.add("bbb");
set.add("ccc");
set.add("ddd");
set.add("ddd");
for (String s : set) {
System.out.println(s);
}
/*aaa
bbb
ccc
ddd*/
}
4.3 Map
4.3.1 什么是Map
>现实生活中,我们每个人都有每个人的身份证号, 像这中一一对象的关系叫做映射, java提供了专门的集合类来存放这些具有映射关系的元素,`java.util.Map` 接口, `Map`和`Collection`存储数据的方式不同, Map是双列的集合结构
- collection集合中元素是独立存在的
- Map 集合中元素是成对出现的, 每个元素都由两个部分组成, 通过键可以找到对应的值
- 'Collection 集合成为单列集合, Map集合称为多列集合(每个键都是唯一的,值可以重复).
4.3.2 Map的常用子类
- HashMap<K,V> : 存储数据采用的哈希表结构, 元素的存取顺序不能保证一致 ,由于要保证键的唯一性,不可重复, 需要重写键的hashcode() 和equals() 方法,
- LinkedHashMap: HashMap 下的子类, 存储数据采用哈希表+链表结构, 通过链表结构可以保证元素存取顺序一致, 通过哈希表结构可以保证键的唯一,不重复, 需要重写键的 hashcode() 和 equals()方法
- Map 集合中都有两个泛型变量 <K,V> , 在定义时 ,两个泛型变量类型可以相同也可以不同, 一般都是这样定义Map<String,Object> 为了方便使用;
具体每个集合的底层以后一一研究 这里就不做赘述, 没和集合底层都是一门学问;
4.3.3 Map 的常用方法
Map 接口中定义了很多种方法, 比较常用的有:
-
public v put(k key ,v value): 把指定的键与指定的值添加到Map中 , 若Map中没有这个K则返回null, 如果有的话就返回原先的值,把新值插进去;
-
public v remove(Object key): 把指定的键所对应的键值在集合中删除, 返回被删除的值
-
public v get(Object key): 根据指定的键, 在Map 集合中获取对应的值
-
public Set keySet(): 获取Map集合中所有的键, 存储到Set 集合中.
-
public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。
-
public boolean containKey(Object key) :判断该集合中是否有此键。
public static void main(String[] args) {
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put(“1”, “test01”);
hashMap.put(“2”, “test02”);
hashMap.put(“3”, “test03”);
hashMap.put(“4”, “test04”);
System.out.println(hashMap);//{1=test01, 2=test02, 3=test03, 4=test04}
Object remove = hashMap.remove(“1”);//test01
System.out.println(remove);
System.out.println(hashMap);//{2=test02, 3=test03, 4=test04}
hashMap.put(“1”, “test01”);
System.out.println(hashMap);//{1=test01, 2=test02, 3=test03, 4=test04}
Object o = hashMap.get(“1”);
System.out.println(o);//test01
Set set = hashMap.keySet();
System.out.println(set);//[1, 2, 3, 4]
Set<Map.Entry<String, Object>> entries = hashMap.entrySet();
System.out.println(entries);//[1=test01, 2=test02, 3=test03, 4=test04]
boolean b = hashMap.containsKey(“1”);
System.out.println(b);//true
}
4.3.4 Map的遍历方式
Map的遍历, 因为键值唯一的,我们可以用keySet() 方法获取到所有的键的set集合,然后遍历set集合根据get()方法将key传进去, 取出值
public static void main(String[] args) {
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("1", "test01");
hashMap.put("2", "test02");
hashMap.put("3", "test03");
Set<String> set = hashMap.keySet();
for (String s : set) {
String ss = (String) hashMap.get(s);
System.out.println(ss);
}
/*test01
test02
test03*/
}
还有一种就是用entrySet()方法, 获取Set集合, 通过遍历set 集合获取每一个entry对象, 在通过entry 的getKey(), 和 getValue()方法来获取键和值;
public static void main(String[] args) {
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("1", "test01");
hashMap.put("2", "test02");
hashMap.put("3", "test03");
Set<Map.Entry<String, Object>> entries = hashMap.entrySet();
for (Map.Entry<String, Object> entry : entries) {
String key = entry.getKey();
Object value = entry.getValue();
System.out.println(key+"==="+value);
}
/*1===test01
2===test02
3===test03*/
}
4.3.5 LinkedHashMap
hashMap的查询速度很快, 可以保证成对元素的唯一性, 但是无法保证元素存放的顺序, 要想保证元素的顺序, 就得使用HashMap下的子类, LikendHashMap 他的底层是采用链表加哈希表组合的数据结构
public static void main(String[] args) {
LinkedHashMap<String, Object> linkedHashMap = new LinkedHashMap<>();
for (int i = 1; i <= 5; i++) {
linkedHashMap.put("" + i + "", "Test" + i);
}
Set<Map.Entry<String, Object>> entries = linkedHashMap.entrySet();
for (Map.Entry<String, Object> entry : entries) {
System.out.println(entry.getKey()+"->"+entry.getValue());
}
/*1->Test1
2->Test2
3->Test3
4->Test4
5->Test5*/
}
4.4 collections工具类
-
java.util.Collections 是集合的工具类,主要用来操作集合
-
public static void shuffle(List<?> list) : 打乱集合顺序.
-
public static void main(String[] args) { ArrayList<Integer> integers = new ArrayList<>(); integers.add(11); integers.add(22); integers.add(33); integers.add(44); System.out.println(integers);//[11, 22, 33, 44] Collections.shuffle(integers); System.out.println(integers);//[11, 33, 22, 44] }
-
-
public static void sort(List list):将集合中的元素按照默认规则排序
-
public static void main(String[] args) { ArrayList<Integer> integers = new ArrayList<>(); integers.add(1); integers.add(97); integers.add(43); integers.add(22); System.out.println(integers);//[1, 97, 43, 22] Collections.sort(integers); System.out.println(integers);//[1, 22, 43, 97] }
-
-
public static void sort(List list , Comparator<? super T>):将集合中元素按照指定规则排序.
public class Student {
private String name;
private int age;public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Strdent{" + "name='" + name + '\'' + ", age=" + age + '}'; } } public static void main(String[] args) { ArrayList<Student> objects = new ArrayList<>(); objects.add(new Student("小王",66)); objects.add(new Student("小红", 1)); objects.add(new Student("小狗", 9)); objects.add(new Student("小猪", 5)); System.out.println(objects); //[Student{name='小王', age=66}, Student{name='小红', age=1}, Student{name='小狗', age=9}, Student{name='小猪', age=5}] Collections.sort(objects, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o2.getAge() -o1.getAge() ; //前减后 由小到大, 后减前 由大到小 } }); System.out.println(objects); //[Student{name='小王', age=66}, Student{name='小狗', age=9}, Student{name='小猪', age=5}, Student{name='小红', age=1}] }
5.泛型, 泛型通配符
5.1 泛型(Geneic)
在JDK1.5之前, 我们定义集合的时候 是可以随意往里面赋值的, 但是取出的时候我们无法确定是什么类型, 有时候就会导致异常的发生java.lang.ClassCasetException, 类型转换异常, 但是才JDK1.5 之后引入了泛型,就不会导致这种异常的发生,因为我们实例集合的时候已经指定了类型,这样的话如果存入的数据类型匹配的话就会导致编译报错
泛型的定义与使用;
可以定义泛型类, 方法, 接口,
-
定义含有泛型类
public class Test01 {
public void add(E e){
}
}
在实例化类的时候在能确定泛型的类型;
public class Test {
public static void main(String[] args) {
Test01<String> stringTest01 = new Test01<>();
stringTest01.add("d");
}
}
-
定义含有泛型方法
//修饰符 <代表泛型的变量> 返回值类型 方法名(参数){}
public class Test02 {
public E query(E e){
return e;
}
}
在调用方法的时候才能确定泛型的类型
Test02 test02 = new Test02();
test02.query("fd");
-
定义含有泛型接口
public interface Test03 {
void add(E e);
E query();
}
调用时指明类型
public class Test implements Test03<String> {
public static void main(String[] args) {}
@Override
public void add(String o) { }
@Override
public String query() { return null; }
}
没有创建时不能确定的类型叫做泛型
public class Test01<E> implements Test03<E>{
public void add(E e){ }
@Override
public E query() { return null; }
}
5.2 泛型通配符
当使用泛型类或者接口是, 传递的数据中,泛型类型不能确定, 可以通过通配符<?> 表示 , 使用通配符后, 只能使用Object中的共性方法 , 集合中元素自身的方法无法使用,
5.2.1 通配符的基本使用
泛型通配符:不知道该用什么类型接收的时候可以使用泛型通配符? 表示未知通配符.此时只能接收数据, 不能我那个集合中存入数据.
public static void main(String[] args) {
LinkedHashMap<Object, Object> objectObjectLinkedHashMap = new LinkedHashMap<>();
get(objectObjectLinkedHashMap);
}
public static void get(Map< ?,? > collection){
}
泛型不存在继承关系
如上图, 这种实例定义泛型定义String 类型, 但是接收用Object 的话就编译报错
5.2.2 通配符高级使用–受限泛型
java中泛型存在上限和下限
泛型的上限
- 格式 类型<? extends 类> 对象名称
- 含义: 只能接收该类型及其子类
泛型的下限
-
格式:类型<? super 类> 对象名称
-
含义:只能接收本类及其父类
public class Test01 {
public static void main(String[] args) {
HashSet objects = new HashSet();
HashSet integers = new HashSet();
HashSet strings = new HashSet();
HashSet numbers = new HashSet();
//getElements(objects); 会编译报错, 因为 getElements 接收的只能是Number类 及其他的子类
//getElements(strings); 同上
getElements(numbers);
getElements(integers);
//getElements1(integers); 编译报错, getElements1 泛型只能接收Number 类型的及其的他父类型
//getElements1(strings);
getElements1(numbers);
getElements1(objects);
}public static void getElements(Collection<? extends Number> collection) { } public static void getElements1(Collection<? super Number> collection) { }
}