API文档
什么是JDK的API
-
JDK中包含大量的API类库,所谓API(Application Programming Interface,应用程序编程接口),就是一些已经写好,可以直接调用的功能.(在Java语言中,这些功能以类的形式封装)
-
JDK API包含类库功能强大,经常使用的有:
字符串操作,
集合操作,
文件操作,
输入输出操作,
网络编程,
多线程,
等待. -
JDK包结构
为了便于使用和维护,JDK类库按照包结构划分,不同功能的类,划分在不同的包中;
经常使用的包如下所示:
包 | 功能 |
---|---|
java.lang | Java程序的基础包,如字符串,多线程等,该包中的类使用频率非常高,不需要import,可以直接使用 |
java.util | 常用工具类,如集合,随机数产生器,日历,时钟等 |
java.io | 文件操作,输入\输出操作 |
java.net | 网络操作 |
java.math | 数学运算相关操作 |
java.security | 安全相关操作 |
java.sql | 数据库访问 |
java.text | 处理文件,日期数字,信息的格式 |
文档注释规范
- 以 /** 开始,以 */ 结束
- 加在类和方法的开头,用于说明作者,时间,版本,要实现功能的详细描述等信息;
- 通过javadoc工具,可以轻松的将此注释转换为HTML文档说明,学习者和程序员主要通过文档了解到API功能
- 文档注释不同于普通注释( // 或者 / * * / ),普通注释写在程序之中,用于程序员进行代码维护和交流,无法通过工具生成文档,而文档注释(/** */)写在类和方法的开头,专门用于生成供API使用者进行参考的文档资料
String及其常用API
- String是不可变对象
- java.lang.String被final修饰,不能被继承
- 字符串底层封装了字符数组及针对了字符数组的操作算法
- 字符串一旦创建,对象就永远无法改变,但字符串引用可以重新赋值
- Java字符串在内存中采用Unicode编码方式,任何一个字符对应两个字节的定长编码
String a = “123”
a 引用变量 指向 “123”
a = “1234”
创建一个新的"1234",然后a指向它
"123"等待回收
- 字符串本质是转换成char[]
- char一个占据两字节,使用Unicode编码格式
String常量池
- Java为了提高性能,使静态字符串(字面量(直接量)/常量/常量连接的结果)在常量池中创建,尽量使用同一个对象,复用静态字符串
- 对于重复出现的字符串字面量,JVM首先会在常量池中查找,如果存在即返回该对象
String a = "1234";
String b = "12"+"34";
//像这样的字符串会在常量池中创建
//这个时候不会在内存中去创建b,而是将a引用的值赋给b
//此时a和b都指向一个对象
== 和equals的区别?
- == 比较基本类型/原生类是比较值,强类型/引用类型是比较引用
- 基本类型只能用==,没有equals方法
- 强类型的equals方法默认是继承自Object类的,该方法默认和==一样,比较引用,如果重写,按照重写后的比较
- Java规定如果重写equals方法,那么也推荐一并重写hashcode方法,保证对象相等的情况下,hashcode也要相等
如果hashcode相等但equals不一定相等,会有hash冲突的情况,会造成使用HashMap,HashSet对象的时候出现错误
内存编码及长度
String在内存中采用Unicode编码,每个字符占用两个字节,任何一个字节(无论是中文还是英文)都算是一个字符长度,占用两个字节
String a = "123 中国 abc";
//同等于
char[] a = {'1','2','3','中','国','a','b','c'}
String str1 = "Hello";
System.out.println(str1.length());//5
Stirng str2 = "你好,String";
System.out.println(str2.length());//9
使用indexOf实现检索
- indexOf方法用于实现字符串中检索另外一个字符串
- String提供几个重载的indexOf方法
- int indexOf(String str) 在字符串中检索str返回其第一次出现的位置如果找不到就返回-1
- int indexOf(Stirng str, int fromIndex) 从字符串的fromIndex位置开始检索
- String还定义了lastIndexOf()方法
int lastIndexOf(String str, int from) str 在字符串中多次出现时将返回最后一个出现位置
String str = "I can because i think i can";
int index = str.indexOf("can");
System.out.println(index);//2index = str.lastIndexOf("can");
System.out.println(index);//24index = str.indexOf("can",6);
System.out.println(index);//24index = str.indexOf("Can");
System.out.println(index);//-1
使用substring获取子串
- substring方法用于返回一个字符串的子类字符串
- substring常用重载方法定义如下:
- String substring(int beginIndex, int endIndex)
返回字符串中从beginIndex(包括)开始到endIndex(不包括)结束的子字符串 - String substring(int beginIndex)
返回字符串中从下标beginIndex(包括)到字符串结尾的子字符串
- String substring(int beginIndex, int endIndex)
public void testSubstring(){String str = "http://www.baidu.com";Stirng subStr = str.substring(11,17);System.out.println(subStr);//baidu.subStr = str.substring(7);System.out.print(subStr);//www.baidu.com
}
trim
去掉一个字符串的前导和后继空字符串
public void testTrim(){String userName = " good man ";userName = userName.trim();System.out.println(userName);//good manSystem.out.println(userName.length());//8
}
charAt
String中定义有charAt()方法
char charAt(int index)方法 charAt()用于返回字符串指定位置的字符,参数index表示指定的位置
public void testCharAt(){String name = "Whatisjava";for(int i= 0 ; i < name.length();i++){char c = name.charAt(i);System.out.print(c+" ")}//W h a t i s j a v a
}
startsWith和endsWith
检查一个字符串是否以指定字符串开头或结尾
public void testStartsWithAndEndsWith(){String str = "Think in Java";System.out.println(str.startsWith("Java"));//falseSystem.out.println(str.endsWith("Java"));//trueSystem.out.println(str.endsWith("a"));//true
}
大小写转换
public void testToUpperCaseAndToLowerCase(){String str = "我喜欢java";str = str.toUpperCase();System.out.println(str);//我喜欢JAVAstr = str.toLowerCase();System.out.println(str);//我喜欢java
}
valueOf
将其他类型转换为字符串类型
public void testValueOf(){double pi = 3.1415926;int value = 123;boolean flag = true;char[] charArr ={'a','b','c','d','e','f'};String str = String.valueOf(pi);System.out.println(str.length());//9str = String.valueOf(value);System.out.println(str.length());//3str = String.valueOf(flag);System.out.println(str.length());//4str = String.valueOf(charArr);System.out.println(str);//abcdefSystem.out.println(str.length());//6
}
toString 和 valueOf的区别
- toString是属于对象的方法
- valueOf是属于类的方法
- 使用toString需要先构建要转换成字符串类型的对象,而使用valueOf则直接使用String点出即可
split方法
- String的split方法可以将字符串按照特定的分隔符拆分成字符串数组
- String[] split(String regexp)参数regexp为正则表达式或字符串,以regexp所表示的字符串为分隔符,将字符串拆分成字符串数组
String str2 = "3,sicheng,3,sicheng@qq.com,33";//拆分:["3","sicheng","3","sicheng@qq.com","33"];String[] strArr = str2.split(",\\s*");for(int i = 0; i < strArr.length;i++){System.out.print(strArr[i]);}
String line = "100+200-150=150";//将数字内容单独提出来组成一个数组String[] strArr = line.split("\\+\\-\\=");//\\+等同于文本+System.out.println(Arrays.toString(strArr));
replaceAll方法
- String提供了用于字符串替换的方法
- String replaceAll(String regexp,String replacement)将字符串中匹配正则表达式regexp的字符串替换为replacement
public void testReplaceableAll(){String str = "abc123asd456ghj789456";//将str中的数字替换为"数字"二字//str = str.replaceAll("[0-9]","数字");str = str.replaceAll("\\d+","数字");System.out.println(str);//abc数字asd数字ghj数字}
StringBuilder及其API
StringBuilder封装可变的字符串,对象创建之后可以通过调用方法改变其封装的字符序列
- StringBuilder有如下常用构造方法:
- public StringBuilder();
- public StringBuilder(String str);
StringBuilder常用方法
方法名 | 功能 |
---|---|
append(String str) | 追加字符串 |
insert(int detOffset,String str) | 插入字符串 |
delete(int start,int end) | 删除字符串 |
replace(int start,int end,String str) | 替换字符串 |
reverse() | 字符串反转 |
StringBuilder
-
StringBuilder的很多方法的返回值均为StringBuilder类型,这些方法的返回语句均为return this
new append(String str).append(return this;)
-
由于改变封装字符串序列后又返回了该对象的引用,可以按照如下简介的方式书写代码
public void testStringBuilder(){//StringBuilder对象的值改变前后地址不变StringBuilder buf = new StringBuilder("seecen");System.out.print(buf.hashCode());System.out.println(buf.append("牛逼").hashCode());buf.append("大神").insert(8,"爱装逼").replace(6,8,"学生");System.out.println(buf);//seecen学生爱装逼 }
StringBuilder总结
- StringBuilder是可变字符串,字符串的内容计算,建议采用StringBuilder实现,这样性能会好一些
- Java的字符串连接过程是利用StringBuilder实现的
String s = "AB"; String s1 = s + "DE" + 1; //等同于 String s2 = new StringBuilder(s).append("DE").append("1").toString();
StringBuilder和StringBuffer
- StringBuilder是非线程安全的,并发处理,性能稍快
- StringBuffer是线程安全的,同步处理,性能稍慢
正则表达式
基本正则表达式
正则表达简介
-
实际开发中,经常需要对字符串数据进行一些复杂的匹配,查找,替换等操作.通过"正则表达式",就可以方便实现字符串的复杂操作
-
正则表达式是一串特定字符(字符串数据类型),组成一个"规则字符串",这个"规则字符串"是描述文本规则的工具,正则表达式就是记录文本规则的代码
字符集合
正则表达式 | 说明 |
---|---|
[abc] | a,b,c中任意一个字符 |
[^abc] | 除了abc以外的任意字符 |
[a-z] | 表示a-z中任意一个字符 |
[a-zA-Z0-9] | 表示a-z,A-Z,0-9中任何一个字符 |
[a-z&&[^bc]] | a-z中除了b,c以外的任意一个字符,其中&&表示"与"关系 |
预定义字符集合
正则表达式 | 说明 |
---|---|
. | 任意一个字符 |
\d | 任意一个数字字符,相当于[0-9] |
\w | 单词字符,相当于[a-zA-Z0-9_] |
\s | 空白字符(并不是只有空格才会产生空白字符) |
\D | 非数字字符 |
\W | 非单词字符 |
\S | 非空白字符 |
数量词
正则表达式 | 说明 |
---|---|
X? | 表示0个或1个X |
X* | 表示1个或者任意多个X |
X+ | 表示1个到多个X(大于等于1) |
X{n} | 表示n个X |
X{n,} | 表示n个到任意多个X(大于等于n) |
X{n,m} | 表示n个到m个X |
练习
检索邮政编码
规则为6位数字
例如:330006
要求:写出匹配邮政编码的正则表达式
- [0-9][0-9][0-9][0-9][0-9][0-9]
- \d\d\d\d\d\d
- \d{6}
分组"()"
- 分组():圆括号表示分组,可以将一系列正则表达式看做一个"整体",分组是可以使用"|"表示"或"关系
录入:匹配手机号码前面的区号:
+0086 12345678911
^(+86|0086)?(\s)*([0-9]{11})$
String 正则API
matches方法
matches(正则表达式)方法:将一个字符串与正则表达式进行匹配如果匹配成功则返回true,否则返回false
String phone = "+86 12345678911";String phoneRegExp = "\\+86?\\s*\\d{11}";System.out.println(phone.matches(phoneRegExp));
“^“和”$”
- 边界匹配
- "^"代表字符串开始
- "$"表示字符串结束
public void testLimit(){//匹配用户名规则-从头到尾连续8~10个单词字符//String reg = "\\w{8,10}";String test = "abcd1234_abcd";//此时test是可以验证通过的,只要字符串存在连续8~10长度的单词字符即可通过验证String reg1 = "^\\w{8,10}$";//此时无法验证通过,要求字符串从头到尾必须符合正则表达式,对字符串整体存在限定boolean flag = test.matches(reg1);System.out.println(flag);//false}
Object
- 在Java类继承结构中java.lang.Object类位于顶端
- 如果定义一个Java类时没有使用extends关键字声明父类,则其父类默认为java.lang.Object
- Object类型的引用可以指定任何类型的对象
Object s1 = new 任意类型对象();
重写toString()方法
- Object类中的重要方法,用于返回对象的值的字符串表示,原则上建议重写
形式如下所示:public String toString(){return "Cell{"+"row="+ this.row+",col="+ this.col +"}"; }
- Java语言中很多地方会默认调用对象的toStirng方法
- 字符串+对象,自动调用对象的toStirng方法
- System.out.println(任意对象),直接调用toString()方法
- 如果不重写toString方法将使用Object的toString方法,其逻辑:类的全称@散列码
- toString方法是非常有用的调试工具
- JDK中的标准类库中,许多类都定义了toString方法,方便用户获得有关对象状态的必要信息
- String重写toString方法就是将自身返回
public String toString(){return this; }
equals方法
- Object中方法,作用在于检测一个对象是否对于另外一个对象
- 在Object类中,这个方法判断两个对象是否具有相同的引用,即是否为相同的对象
- 在实际中,一般需要重写该方法,通过比较对象的成员属性,使该方法更加有意义
- 没有重写的equals方法等同于==
重写equals
public boolean equals(Object obj){if(obj==null){return false;}if(this == obj){return true;}if(obj instanceof Cell){Cell cell = (Cell)obj;return cell.row == this.row && cell.col == this.col;}else{retuen false;}
}
包装类
包装类概述
- 在进行类型转换的范畴内,有一种特殊的转换,需要将int这样的基本类型转换为对象
- 所以所有基本类型都有一个与之对应的类,即包装列类
- 包装类是不可变的,在构造了包装类对象后,不允许更改包装在其中的值
- 包装类是final的,不能定义他们的子类
基本类型 | 包装类 | 父类 |
---|---|---|
int | java.lang.Integer | java.lang.Number |
long | java.lang.Long | java.lang.Number |
double | java.lang.Double | java.lang.Number |
short | java.lang.Short | java.lang.Number |
float | java.lang.Float | java.lang.Number |
byte | java.lang.Byte | java.lang.Number |
char | java.lang.Character | java.lang.Object |
boolean | java.lang.Boolean | java.lang.Object |
Number及其主要方法
-
抽象类Number 是Byte,Short,Integer,Long,Float,Double的父类
-
Number的子类必须要提供将表示的数值转换为byte,short,int,long,float,double的方法 ,例如:
doubleValue()以double形式返回指定的数值intValue()以int形式返回指定的数组floatValue()以float形式返回指向的数组
包装类可以通过对应的方法将自己表示的值转换为6种基本类型
自动装箱和自动拆箱
- 从Java5.0版本以后加入autoboxing功能
- 自动"装箱"和"拆箱"是依靠JDK5的编译器在编译时的"预处理"工作
- 例如下列代码写法均为正确形式:
Integer a = 100;//装箱Integer b = 200;//装箱Integer c = a+b;//先拆箱后装箱double d = c;//拆箱```
ps:一个基本类型可以转换为任意包装类型,但是可能会造成精度损失和溢出
Number d = 123.45;
Number n = 123;
//输出d和n对象所属类型
System.out.println(d.getClass().getName());//java.lang.Double
System.out.println(n.getClass().getName());//java.lang.Integer
int intValue = d.intValue();
double doubleValue = n.doubleValue();
//可以将基本类型包装成不同的类
double c = 123.45;
Double c1 = Double.valueOf(c);
Integer c2 = Integer.valueOf((int)c);
Long c3 = Long.valueOf((long)c);
- 装箱和拆箱是"编译器"认可的,而不是虚拟机,编译器在生成字节码时插入必要的方法调用
- 在进行自动装箱和拆箱的过程中,Integer的变量自动调用intValue,Double类型自动调用doubleValue,以此类推
- 基本类型到包装类要遵循基本类型之间的转换关系
Integer.valueOf(12.12);//这要会报错
Double.valueOf(12);//这样可以
Integer常用功能
- 该类提供了多个方法,能在int类型和String类型之间相互转换,还提供一些常量,例如:
static int MAX_VALUE // 值为2的31次方-1,表示int类型的最大值
static int MIN_VALUE // 值为-2的31次方,表示int类型的最小值
//Integer 的静态方法parseInt用于将字符串转换为int
String str = "123";
int value = Integer.parseInt(str);
System.out.println(value);
//转换的前提是str是一个数值类型字符串
str = "一二三四";//NumberFromatException;数据格式转换异常
value = Integer.parseInt(str);
System.out.print(value);
Double常用功能
在Double类的对象中包装一个基本类型double值
//构造方法
Double(double value)
Double(String str)
方法
- double doubleValue() 返回此Double对象的double值
- static double parseDouble (String str)返回一个新的double值,该值被初始化为用指令的String表示的值
String str = "12345.0000";
double value = Double.parseDouble(str);
System.out.println(value);//12345.0str = "12345.00";
value = Double.parseDouble(str);
//获取Double类型的对象
Double a = Double.valueOf(12.6);
//等价
Double b = new Double(12.6);
//等价
Double c = new Double("12.6");//将会自动将"12.6"转换为double类型的12.6
//其他包装类同理
包装类到基本类型
- 包装类可以转换为6种基本类型,但需要显示的调用,否则也要遵循基本类型之间的转换关系
Integer a = 100;
//int b = a;
//double b = a.doubleValue();
double b = a.intValue();Double a1 = 100.0;
//double b1 = a1;
//int b1 = a1.intValue();
int b1 = a1.doubleValue();
int b1 = (int)a1.doubleValue();
SimpleDateFormat简介
- java.text.SimpleDateFormat是一个与语言环境有关的方式来格式化和解析日期的具体类
- 它允许进行格式(日期–>文本),解析(文本–>日期)和规范
构造方法
SimpleDateFormat()
SimpleDateFormat(String parrtern)用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat
方法
- final String format(Date date) Date ⇒ String
- Date parse (String sourse) throws ParseException String ⇒ Date
日期模式匹配字符
字符 | 含义 | 示例 |
---|---|---|
y | 年 | yyyy年------2013年;yy----13年 |
M | 月 | MM月-----01月;M月—1月 |
d | 日 | dd日–06日;d日----6日 |
a | am或pm标识 | a—下午(PM) |
H | 小时(24小时制) | H时---- 13时 |
h | 小时(12小时制度) | a h时----下午 12时 |
m | 分钟 | HH:mm:ss------12:23:12 |
s | 秒 | hh(a):mm:ss-------12(下午):46:12 |
将String解析为Date
Date(Mon Apr 25 15:15:44 CST 2022) ⇒ SimpleDateFormat(HH:mm:ss)⇒ String 12:34:23
public void testParse() throws ParseException{//将String解析为Date//parse()方法用于按照特定格式将表示时间的String转换为Date对象String str = "2013-12-25";//创建SimpleDateFormatSimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");Date date = sdf.parse(str);System.out.println(date); /*try{Date date = sdf。parse(str);}catch(ParseException e){System.out.println("转不了");}*/
}
将Date格式化为String
public void testFormat(){Date date = new Date();//Wed Dec 25 00:00:00 CST 2013SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String dateStr = sdf.format(date);System.out.println(dateStr);//2022-4-24 16:21:10
}
Calendar简介
- java.util.Calendar类用于封装日历信息,其主要作用在于其方法可以对时间分量进行运算
- Calendar是抽象类,其具体子类针对不同国家的日历系统,其中应用最广泛的是GregorianCalendar(格里高历,阳历),对应世界上绝大多数国家/地区使用的标准日历系统
getInstance方法
- Calendar 提供了一个类方法getInstance,以获取此类型的一个通用的对象
- Calendar 的getInstance 方法会返回一个Calendar对象,其日历字段已由当前日期和时间初始化
- Calenda c = Calendar.getInstance();
public void testGetInstance(){Calendar c = Calendar.getInstance();//输出Calendar对象所属的实际对象System.out.println(c.getClass()/getName());//getTime方法返回对应的Date对象System.out.println(c.getTime());//c.getTime()等同于new Date();//GregorianCalendar的默认信息System.out.println(c);//java.util.GregorianCalendar[time=1651026754258,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=3,WEEK_OF_YEAR=18,WEEK_OF_MONTH=5,DAY_OF_MONTH=27,DAY_OF_YEAR=117,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=4,AM_PM=0,HOUR=10,HOUR_OF_DAY=10,MINUTE=32,SECOND=34,MILLISECOND=258,ZONE_OFFSET=28800000,DST_OFFSET=0]//创建GregorianCalendar并初始化GregorianCalendar c1 = new GregorianCalendar(2013,Calendar.DECEMBER,25);}
设置时间及分量
public void testSet(){Calendar c = Calendar.getInstance();c.set(Calendar.YEAR,2014);c.set(Calendar.MONTH,4);//表示当前月份数值加一c.set(Calendar.DATE,5);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-HH-dd HH:mm:ss");System.out.println(sdf.format(c.getTime()));//如果不是设置时分秒,则默认为系统当前时间
}
- void add(int intValu,int mount);为给定的时间分量的值加上给定的值,若给定的值为负数则是减去给定的值
获取时间及分量
- final static int YEAR:指示年份的字段数字(1)
- final static int MONTH:指示月份的字段数字(2)
- final static int DATE:指示一个月中的第几天(3)
- final static int DAY_OF _WEEK:指示一个星期的第几天(7)
使用方法:在get()方法中传入上面的常量
- int getActualMaximum(int intValues): 传入calendar类的时间值(如:Calendar.DATE)返回给定日历字段的最大值
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR,2014);
for(int month = Calendar.JANURAY;month <= Calendar.DECEMBER;month++ ){c.set(Calendar.MONTH,month);System.out.println(c.getActualMaximum(Calendar.DATE) + " ");//输出2014年每个月的最大值
}
setTime 与getTime
- Date getTime() : 使用Date描述Calendar表示的日期并返回
- void setTime(Date date) : 使用Calendar表示Date所描述的日期
Calendar c = Calendar.getInstance();
Date date = new Date(1600800456332L);
c.setTime(date);
集合
List 和set
在实际开发中,需要将使用的对象存储与数据结构的容器中,JDK提供了这样的容器--------集合(Collection);Collection是一个接口,定义了集合相关的操作方法其有两个子接口List,set
- List : 可以重复集
- set : 不可重复集
- 不可以放基本数据类型
- set在判断是否重复时,会先调用hashCode方法,如果hashCode不存在则插入元素,如果hashCode存在则调用该对象类的equals方法,返回true则说明对象已经存在,返回false则插入元素。
集合持有对象的引用,集合中存储的都是引用类型元素,并且集合中只保存每个元素对象的引用,而并非将元素对象本身存入集合
Collection<Cell> cells = new ArraysList<Cell>();
cells.add(new Cell(1,3));
Cell cell = new Cell(33,2);
cells.add(cell);
System.out.println(cells);cell.drop();
System.out.println(cells);//输出发生改变(多个引用指向一个对象)
- boolean add(E e) : 该方法将给定的元素添加进集合,若添加成功则返回true,否则返回false
- boolean contains(Object object) : 该方法用于判断给定的元素是否包含在集合中,若包含则返回true,否则返回false .
Collection<String> c = new ArrayList<>();
c.add("a");
String str = "a";
System.out.println(c.contains(str));//true
- int size() :返回当前集合中的元素的总数
- void clear() : 清空当前集合
- boolean isEmpty() : 判断当前集合是否包含任何元素
Collection<String> c = new HashSet<>();
c.add("a");
c.add("b");
c.add("a");
System.out.println(c.isEmpty);//false
System.out.println(c);//[a,b]
System.out.println(c.size());//2
c.clear();
System.out.println(c);//[]
- boolean addAll(Collection<?extends E> c) : 该方法需要传入一个集合,并将该集合中的所有元素添加到当前集合中
- boolean containsAll(Collection<?> c) : 判断当前集合是否包含给定集合中的所有元素,若包含则返回true
Collection<String> c1 = new ArrayList<>();
c1.add("a");
c1.add("b");
Collection<String> c2 = new ArrayList<>();
c2.addAll(c1);
System.out.println(c2);//[a,b]
Collection<String> c3 = new ArrayList<>();
c3.add("a");
System.out.println(c1.containAll(c3));//true
Iterator
迭代器用于遍历集合元素,获取迭代器可以使用Collection定义的方法(Iterator iterator()); 迭代器Iterator是一个接口,集合在重写Collection 的 iterator()方法是利用内部迭代器实现;Iterator提供了统一的遍历集合的方式,其提供了用于遍历集合的两个方法:
- boolean hasNext() : 判断集合是否还有元素可以遍历
- E next() : 返回迭代的下一个元素
Collection<String> c1 = new Hashset<>();
c1.add("a");
c2.add("b");Iterator<String> it = c1.iterator();
while(it.hasNext()){String str = it.next();System.out.println(str);
}
- void remove() : 在使用迭代器遍历集合时,不能通过集合的remove() 删除元素,否则会抛出并发修改异常,我们可以通过迭代器自身提供的remove()来删除通过next()迭代出的元素;迭代器删除方法是在源集合中删除元素; 需要注意的是,在调用remove()前必须通过迭代器的next()迭代过元素,那么删除的就是这个元素,并且需要再次使用next()后才可以再次调用remove()
Collection<String> c1 = new HashSet<>();
c1.add("a");
c1.add("b");Iterator<String> it = c1.iterator();
while(it.hasNext()){String str = it.next();if(str.startWith("a")){it.remove();}
}
增强型for循环
Java5.0之后推出了一个新特性,增强for循环,也称为新循环,该循环不通用于传统的循环工作,其只用于遍历集合或数组;新循环并非新的语法,而是在编译过程中,编译器会将新循环转化为迭代器模式,所有新循环本质上是迭代器
for(元素类型 e : 集合或数组) {循环体
}Collection<String> c1 = new HashSet<>();
c1.add("a");for(String str : c1) {System.out.println(str);
}
泛型机制
泛型是JavaSE 5.0引入的特性,泛型的本质是参数化类型,在类,接口和方法的定义过程中,所操作的数据类型被传入的参数指定;Java泛型机制广泛的应用在集合框架中,所有集合类型都带有泛型参数,这样在创建集合时可以指定放入集合中元素的类型,Java编译器可以据此进行类型检查,这样可以减少代码在运行时出现的错误可能性
ArrayList类的定义中,中的E为泛型参数,在创建对象时可以将类型作为参数传递,此时类中定义的所有E将被替换为传入的参数
集合操作(线性表)
ArrayList和LinkedList :
-
List接口是Collection的子接口,用于定义线性表数据结构,
-
可以将List理解为存放对象的数组,只不过其元素个数可以动态的增加或减少,
-
List接口的两个常见实现类ArrayList和LinkedList,分别用于动态数组和链表的方式实现了List接口,
-
可以认为ArrayList和LinkedList方法在逻辑上完全一样,只是在性能上有一定的差别,ArrayList更适合用于随机访问而LinkedList更适合用于插入和删除
-
List : 定义线性表的数据结构,ArrayList 和LinkedList都是线性表结构(线性:有顺序,一个接一个)
-
ArrayList 和 LinkedList方法相同,对于调用者而言没有区别,但是底层逻辑不同,导致性能不同
ArrayList 和LinkedList的底层逻辑(底层结构)
-
ArrayList : 动态数组结构
-
LinkedList : 链表结构
上述方法都是线性结构,都可以理解为:存放对象的数组,只不过可以动态改变
常用方法
List除了继承Collection定义的方法外,还根据其线性表的数据结构定义了一系列的方法,其中最常用的就是基于下标的get 和set方法:
- E get(int index):获取集合中指定下标对应的元素,下标从0开始
- E set(int index,E element) :将给定元素存入给定位置,并将原位置的元素返回
List<String> c1 = new ArrayList<>();
c1.add("a");
c1.add("b");
c1.set(0,c1.set(1,c1.get(0)));//交换a,b在集合中的位置
- void add(int index,E element) : 将给定的元素插入到指定位置,原位置及后续元素都顺序向后移动
- E remove(int index) : 删除给定位置的元素,并将被删除的元素返回
- List subList(int fromIndex,int toIndex) : 用于获取子List,截取子List的首尾下标(前包括,后不包括); 需要注意的是,subList 获取的 List 与原 List 占有相同的存储空间,对子 List 的操作会影响原 List
List<Integer> c1 = new ArrayList<>();
for(int i = 0 ; i < 4; i++){c1.add(i);
}//c1:[0,1,2,3]
List<Integer> subList = c1.subList(1,3);//submit : [1,2]
subList.clear();//c1 :[0,3]
List转化为数组
List的toArray方法用于将集合转化为数组,但实际上该方法是Collectoin中定义,所以所有的集合都具备这个功能
-
其中有两个方法:
- Object[] toArray();
- T[] ToArray(T[]a)
其中第二个方法是比较常用的,我们可以传入一个指定类型的数组,该数组的元素类型应与集合的元素类型一致,返回值是转换后的数组
public void testListArray(){List<String> list = new ArrayList<String>();list.add("a");list.add("b");list.add("c");list.add("d");//Strin[] a1 = new String[]{};String strArr = list.toArray(new String[]{});//1.存入的数组相同可以指定长度,如果长度大于集合的元素个数,则转换后的数组长度与传入数组相同,并且使用null填充数组//2.如果传入的数组长度小于集合元素,则转换后的数组长度为集合元素的长度System.out.println(Arrays.toString(strArr));//[a,b,c,d]
}
数组转换为List
Arrays类中提供了一个静态方法asList,使用该方法我们将一个数组转换为对应的List集合
- 其方定义为:
- static list asList<T…a>
- 返回的List的集合元素类型由传入是数组的元素类型决定
- 并且要注意的是,返回的集合我们不能对其增删元素,否则会抛出异常,并且对集合的元素进行修改会影响数组对应的元素
public void testArrayToList(){String[] strArr = {"A","B","C","D"};List<String> list = Array.asList(strArr);System.out.println(list);//list.add("E");//UnsupportedOprationExceptionSystem.out.println(list.getClass().getName());//java.util.Arrays$ArrayListList<String> list1 = new ArraysList<String>();list1.addAll(Arrays.asList(strArr));
}
List排序
Collections.sort方法实现排序
Collections是集合和工具类,它提供了很多便于我们操作集合的方法,其中就有操作集合排序的sort方法
- 方法定义为:
-void sort(list)
该方法的作用是对给定的集合元素进行自然排序
public void testSort(){List<Integer> list = new ArrayList<Integer>();Random r = new Random();for(int i = 0 ; i < 10 ;i++){list.add(r.nextInt(100))}System.out.println(list);Collections.sort(list);System.out.println(list);
}
Comparable
- Collections 的sort方法是对集合元素进行自然排序,那么两个元素就一定要有大小之分.
- 这个大小之分如何界定呢?
- 实际上,在使用Collections的sort排序的集合都必须是Comparable接口的实现类,该接口表示其子类是可以比较的,因为实现该接口必须重写抽象方法
int comparableTo(T t)
该方法用于使用对象与给定对象进行比较
若当前对象大于给定对象,那么返回值应为>0的整数
若当前对象小于给定对象,那么返回值应为<0的整数
若当前对象等于给定对象,那么返回值因为0List<Cell> list = new ArrayList<Cell>(); list.add(new Cell(1,1)); list.add(new Cell(5,5)); list.add(new Cell(2,2)); list.add(new Cell(3,3)); list.add(new Cell(6,6)); list.add(new Cell(4,4)); System.out.println(list); Collections.sort(list); System.out.println(list)public class Cell implements Comparable<Cell>{@Overridepublic int compareTo(Cell o){return this.row - o.row;} }
Comparator
- 一旦Java类实现了Comparable接口,其比较逻辑就已经确定,如果希望在排序的操作中临时指定比较规则,可以采用Comparator接口的回调方式
- Comparator接口要求实现类必须重写其定义方法
- int compare(T o1,T o2)
- 该方法的返回值要求
- 若o1 > o2则返回值应>0
- 若o1 < o2则返回值应<0
- 若o1 == o2则返回值应为0
List<Cell> list = new ArrayList<Cell>();
list.add(new Cell(8,1));
list.add(new Cell(4,5));
list.add(new Cell(2,2));
//按照col值的大小排序
Collections.sort(list,new Comparator<Cell>(){@Overridepublic int compare(Cell o1,Cell o2){return o1.getCol() - o2.getCol();}
});
System.out.println(list);
队列和栈
Queue
- 队列(Queue)是常用的数据结构,可以将队列看成特殊的线性表,队列限制了对线性表的访问方式 : 只能从线性表的一段添加(offer)元素,从另一端取出(poll)元素
- 队列遵循先进先出(FIFO:First input First Output)的原则
- JDK中提供了Queue接口,同时使用LinkedList实现了该接口(选择LinkedList实现Queue的原因在于Queue经常需要进行添加和删除操作,而LinkedList在这方面效率比较高)
Queue接口主要方法
- boolean offer(E e):将一个对象添加至队尾,如果添加成功则返回true
- E poll() : 从队首删除并返回一个元素
- E peek() : 返回队首元素(但并不删除)
public viod testQueue(){Queue<String> queue = new LinkedList<String>();queue.offer("a");queue.offer("b");queue.offer("c");queue.offer("d");System.out.println(queue);//[a,b,c,d]while(queue.size() > 0) {String str = queue.poll();System.out.println(str+" ")}System.out.println("最后" + queue);//空了
}
Deque
- Deque是Queue的子接口,定位了所谓的"双端队列"即从队列的两端分别可以入队(offer)和出队(poll),LinkedList实现了该接口
- 如果将Deque限制为只能从一端入队和出队,则可实现"栈(Stack)“的数据结构,对于栈而言,入栈称之为"push”,出栈称之为"pop"
- 栈遵循先进后出(FILO:First Input Last Output)的原则
栈Stack
Java集合没有单独的Stack接口,因为有个遗留类的名字就叫Stack,出于兼容性考虑,没有办法创建Stack接口,能用Deque接口"模拟"一个Stack
当我们把Deque接口作为栈使用时,注意只调用push()/pop()/peek()方法,不要调用addFirst()/removeFirst()/peekFirst…
Deque<String> stack = new LinkedList<String>();
//先进后出
stack.push("a");
stack.push("b");
stack.push("c");
stack.push("d");
System.out.println(stack);//[d,c,b,a]
while(stack.size() > 0) {Stirng str = stack.pop();System.out.println(str + " ");//d c b a
}
System.out.println(stark);//[]
线性存储和链式存储的区别
- Java中线性存储的数据结构有 : 数组,ArrayList
他们的优点是:查找访问快,缺点是在非末尾的位置删除和插入效率低 - Java中链表/表存储的数据结构有 : LinkedList
它们的优点是: 插入和删除效率高,缺点是访问头尾的位置效率低
常用数据结构的理解
- ArrayLisrt 和 LinkedList实现自List接口,基类接口为Collection
- HashSet和TreeSet是实现自Set接口,基类接口为Collection
- ArrayList 和 LinkedList 有序,可重复
- HashSet无序,不可重复
- TreeSet排序(存放的排序数据需要实现Comparable接口),不可重复
Map接口
Map接口定义的集合又称为查询表用于存储所谓"Key-Value"的映射对,Key可以看成是Value的索引,作为Key的对象在结合中不可以重复
根据内部数据结构的不同,Map接口有多种实现类,其中常用的有内部为hash表实现的HashMap和内部为排序二叉树实现的TreeMap.
小结:
- 存储是Key-Value形式
- Key不能重复
put()方法
-
Map接口中定义了向Map中存放元素的put方法
-
V put(K key,V value)
- 将Key-Value对存入Map,如果在结合中已经包含该Key,则操作将替换该Key所对应的Value,返回值为Key原来所对应的Value(如果没有则返回null)
get()方法
- Map接口定义了从Map中获取元素的get方法
- V get(Object key)
- 返回参数key所对应的Value对象,如果不存在则返回null
ConatinsKey()方法
-
Map接口中定义了判断某个key是否在Map中
-
boolean containsKey(Object key)
-
若Map中包含给定的key则返回true,否则返回false
@Test
public void testPut(){Map<String,Emp> emplyees = new HashMap<String, Emp>();//向map中添加元素emplyees.put("张三",new Emp("张三",25,'男',5000));emplyees.put("李四",new Emp("李四",24,'女',3300));System.out.println(emplyees.size());Emp emp1 = emplyees.put("李四",new Emp("李四光",56,'男',300));System.out.println(emplyees.size());System.out.println(emp1);Emp emp2 = emplyees.get("李四");System.out.println(emp2);boolean has = emplyees.containsKey("王二");System.out.println("是否有员工王二:" + has );}
hashCode()方法
-
从HashMap的原理中我们可以看到,key的hashCode方法的返回值对HashMap存储元素时会起着很重要的作用,而hashCode()方法实际上是在Object中定义的,那么应该妥善重写该方法
-
对于重写了equals方法的对象,一般要妥善的重写继承自Object类的hashCode()方法(Object提供的hashCode方法将返回该对象所在内存地址的整数形式)
-
重写hashCode方法需要注意以下两点:
-
其一,与equals方法的一致性,即equals比较返回true的两个对象其hashCode方法的返回值应该相同
-
其二,hashCode返回的数值应该符合hash算法的要求,试想如果有很多对象的hashCode方法返回值都相同,则会大大降低hash表的效率,一般情况下可以使用IDE(如 Eclipse,idea提供的工具自动生成hashCode方法
-
目前的理解:
相同的类型==> hashCode值应该相等
缺点:数据一旦变大,会很大程度降低hash表的效率
查看重写的hashCode方法
是否根据对象的属性值进行重写
hashCode方法中用到的属性是否也是equals比较时用到的属性
capacity:容量,hash表里的bucket(桶)的数量,也就是散列数组的大小
initial capacity:初始容量,创建hash表时,初始bucket的数量,默认构建容量是16
size : 大小,当前散列表中存储数据的数量
Loadfactor : 加载因子,默认值是0.75(也就是75%),当散列表增加数据时,如果size/capacity的值大于Loadfactor则发生扩容并且重新散列(rehash)
性能优化 : 加载因子较小时,散列查询性能会提高,同时也浪费散列桶空间容量,0.75是性能和空间相对平衡的结果,在创建散列表时指定合理容量,减少rehash提高性能
Map的遍历
使用keyset()方法
- Map提供了三种遍历方式
- 遍历所有的key
- 遍历所有的key-value
- 遍历所有的value(不常用)
遍历所有的Key
- Set keySet()
该方法会将当前Map中所有的key存入一个Set集合后返回
//遍历Set<String> keys = map.keySet();for(String key:keys){System.out.println(key + ":" + map.get(key));}
遍历所有的键值对的方法
- Set<Entry<K,V>> entrySet();
该方法会将当前Map中的每一组key-value对封装为一个Entry对象并存入一个Set集合后返回
//使用entrySet方法遍历
Set<Map.Entry<String,Integer>> entrys = map.entrySet();
for(Map.Entry<String,Integer> entry : entrys){System.out.println(entry.getKey()+":"+entry.getValue());
}
hashmap底层实现原理
-
hashmap底层是由数组 + 链表 + 红黑树实现
-
put原理
- 根据key计算hash,找到存储的数组位置
- 如果该位置为null,直接存储进去
- 如果不为null,判断key的equals是否相等,如果相等则覆盖并返回旧的数据,如果如果key不相等,那么会把新的数据以链表格式存储到该位置的末尾.当链表的长度超过8的时候会转换为红黑树(因为链表的长度太长没有红黑树高效).
-
get原理
- 根据key计算hash,找到要获取数据的位置
- 判断位置是否为null,为null返回null
- 不为null,循环链表或红黑树逐个判断equals是否相等,相等返回值,否则返回null
-
扩容,默认hashmap底层大小为16,扩容因子为0.75,每次扩容2倍.