Java高级API(上)

本文主要探讨了Java的高级API,包括String类的常用方法,如indexOf、substring、trim等,以及正则表达式的基本使用。讲解了如何通过split、replaceAll进行字符串处理,并介绍了StringBuilder及其与StringBuffer的区别。此外,文章还涵盖了Object的toString和equals方法,包装类的自动装箱与拆箱,以及日期处理相关的SimpleDateFormat和Calendar类。最后,讨论了集合框架中的List、Set、Map接口及其相关实现类的特性和操作,如ArrayList、LinkedList、HashMap等,并提到了泛型和数据结构的理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

API文档

什么是JDK的API

  1. JDK中包含大量的API类库,所谓API(Application Programming Interface,应用程序编程接口),就是一些已经写好,可以直接调用的功能.(在Java语言中,这些功能以类的形式封装)

  2. JDK API包含类库功能强大,经常使用的有:
    字符串操作,
    集合操作,
    文件操作,
    输入输出操作,
    网络编程,
    多线程,
    等待.

  3. JDK包结构
    为了便于使用和维护,JDK类库按照包结构划分,不同功能的类,划分在不同的包中;

经常使用的包如下所示:

功能
java.langJava程序的基础包,如字符串,多线程等,该包中的类使用频率非常高,不需要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的区别?

  1. == 比较基本类型/原生类是比较值,强类型/引用类型是比较引用
  2. 基本类型只能用==,没有equals方法
  3. 强类型的equals方法默认是继承自Object类的,该方法默认和==一样,比较引用,如果重写,按照重写后的比较
  4. 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方法
    1. int indexOf(String str) 在字符串中检索str返回其第一次出现的位置如果找不到就返回-1
    2. 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);//2

index = str.lastIndexOf("can");
System.out.println(index);//24

index = str.indexOf("can",6);
System.out.println(index);//24

index = str.indexOf("Can");
System.out.println(index);//-1

使用substring获取子串

  • substring方法用于返回一个字符串的子类字符串
  • substring常用重载方法定义如下:
    1. String substring(int beginIndex, int endIndex)
      返回字符串中从beginIndex(包括)开始到endIndex(不包括)结束的子字符串
    2. String substring(int beginIndex)
      返回字符串中从下标beginIndex(包括)到字符串结尾的子字符串
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 man
	System.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"));//false
	System.out.println(str.endsWith("Java"));//true
	System.out.println(str.endsWith("a"));//true
}

大小写转换

public void testToUpperCaseAndToLowerCase(){
	String str = "我喜欢java";
	str = str.toUpperCase();
	System.out.println(str);//我喜欢JAVA
	str = 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());//9
	
	str = String.valueOf(value);
	System.out.println(str.length());//3

	str = String.valueOf(flag);
	System.out.println(str.length());//4
	
	str = String.valueOf(charArr);
	System.out.println(str);//abcdef
	System.out.println(str.length());//6
}

toString 和 valueOf的区别

  1. toString是属于对象的方法
  2. valueOf是属于类的方法
  3. 使用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有如下常用构造方法:
    1. public StringBuilder();
    2. 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
  1. StringBuilder是非线程安全的,并发处理,性能稍快
  2. 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
要求:写出匹配邮政编码的正则表达式

  1. [0-9][0-9][0-9][0-9][0-9][0-9]
  2. \d\d\d\d\d\d
  3. \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的,不能定义他们的子类
基本类型包装类父类
intjava.lang.Integerjava.lang.Number
longjava.lang.Longjava.lang.Number
doublejava.lang.Doublejava.lang.Number
shortjava.lang.Shortjava.lang.Number
floatjava.lang.Floatjava.lang.Number
bytejava.lang.Bytejava.lang.Number
charjava.lang.Characterjava.lang.Object
booleanjava.lang.Booleanjava.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);
int
Integer
String

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.0

str = "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

日期模式匹配字符

字符含义示例
yyyyy年------2013年;yy----13年
MMM月-----01月;M月—1月
ddd日–06日;d日----6日
aam或pm标识a—下午(PM)
H小时(24小时制)H时---- 13时
h小时(12小时制度)a h时----下午 12时
m分钟HH:mm:ss------12:23:12
shh(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";
	//创建SimpleDateFormat
	SimpleDateFormat 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 2013
	SimpleDateFormat 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");//UnsupportedOprationException
	System.out.println(list.getClass().getName());//java.util.Arrays$ArrayList

	List<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的整数
    若当前对象等于给定对象,那么返回值因为0

    List<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>{
    	@Override
    	public 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>(){
	@Override
	public 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);//[]

线性存储和链式存储的区别

  1. Java中线性存储的数据结构有 : 数组,ArrayList
    他们的优点是:查找访问快,缺点是在非末尾的位置删除和插入效率低
  2. 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.

小结:

  1. 存储是Key-Value形式
  2. 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方法

  1. 是否根据对象的属性值进行重写

  2. 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底层实现原理

  1. hashmap底层是由数组 + 链表 + 红黑树实现

  2. put原理

    1. 根据key计算hash,找到存储的数组位置
    2. 如果该位置为null,直接存储进去
    3. 如果不为null,判断key的equals是否相等,如果相等则覆盖并返回旧的数据,如果如果key不相等,那么会把新的数据以链表格式存储到该位置的末尾.当链表的长度超过8的时候会转换为红黑树(因为链表的长度太长没有红黑树高效).
  3. get原理

    1. 根据key计算hash,找到要获取数据的位置
    2. 判断位置是否为null,为null返回null
    3. 不为null,循环链表或红黑树逐个判断equals是否相等,相等返回值,否则返回null
  4. 扩容,默认hashmap底层大小为16,扩容因子为0.75,每次扩容2倍.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值