目录
数据类型
基本数据类型
引用数据类型
基本数据类型和引用数据类型的比较
原文链接:基本数据类型和引用数据类型的区别_Brandon懂你的博客-CSDN博客_引用数据类型和基本数据类型区别
修饰符
this关键字
-
引用属性:
例:
class ThisTest{ private int x,y; public test(int x,int y){ this.setX(x); //this可以省略 } void setX(int x){ this.x=x; //this.x是该对象的属性x,等号后的x是setX()方法中的参数 } }
-
引用构造方法:构造方法的this指向同一个类中不同参数列表的另外一个构造方法。
public class Test{ String name; Test(String name){ this.name=name; //1.中引用属性 } Test(){ this("John/Mary Doe"); //2.中引用构造方法 } public static void main(String args[]){ Test t1 = new Test("digger"); Test t2 = new Test(); } }
注 : 构造方法中,如果要使用关键字this,那么必须放在第一行,否则会出现编译错误。
static关键字
概述
多个对象共享同一份数据。
一个类的不同对象有些共享的数据,这样我们就可以使用static来修饰。
一旦使用了static关键字,那么这样的内容不再属于对象,而是属于类的,所以凡是本类的对象,都共享同一份。
可以用来修饰成员变量,修饰成员方法,以及静态代码块儿。
原文链接:static关键字详解_黄大仙Ol的博客-CSDN博客_static关键字
final关键字
修 饰 符:
1,final是一个修饰符,可以修饰类,方法,变量
2,final修饰的类不可以被继承
3,final修饰的方法不可以被覆盖
4,final修饰的变量是一个常量,只能赋值一次(程序中一个数值固定,直接使用这个数值阅读性差,
所以给改数据起个名而且值不能变化,所以用final固定)
写法规范:常量所有字母都大写,多个单词,中间用_连接
类的属性的可见性修饰符
名称 | 说明 | 备注 |
---|---|---|
public | 可以被任何类访问 | |
protected | 可以被同一包中的所有类访问,可以被所有子类访问 | 子类没有在同一包中也可以被访问 |
private | 只能被当前类的方法访问 | |
缺省的 | 可以被同一包中的所有类访问 | 如果子类没有在同一包中,不能访问 |
类的方法的可见性修饰符
名称 | 说明 | 备注 |
---|---|---|
public | 可以被任何类访问 | |
protected | 可以被同一包中的所有类访问,可以被所有子类访问 | 子类没有在同一包中也可以被访问 |
private | 只能被当前类的方法访问 | |
缺省的 | 可以被同一包中的所有类访问 | 如果子类没有在同一包中,不能访问 |
类与对象
类的定义
在Java里的类分为两种——系统类和自定义类。
系统类是Java自带的标准类,如:System、String、BufferReader。
用class关键字定义类
[public] [abstract] [final] class [extends 父类] [implements 接口]{
}
类修饰符:
-
public:公有类
-
abstract:抽象类
-
final:终结类
对象的创建
类是定义一个对象数据和方法的蓝本。对象是类的实例,可以从一个类中创建许多实例(对象)。
创建一个实例的过程叫做实例化。
例:有一个类名为狗类,他的一个实例化对象可以为泰迪。
构造函数
构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们,即构造函数的重载。
构造子类对象时,访问子类构造函数,父类也运行,因为子类构造函数中第一行有一个默认的隐式语句super( )
子类的实例化过程:子类中所有的构造函数默认都会访问父类中的空参数的构造函数
为什么子类实例化的时候要访问父类中的构造函数:
因为子类继承类父类,获取到了父类中的内容(属性),所有在使用
父类内容之前要先看父类是如何对自己的内容进行初始化的,所以子
类在构造对象时,必须访问父类中的构造函数,在子类构造函数中加
入了super( )语句。
如果父类中没有定义空参数构造函数,那么子类的构造函数必须用super
明确调用父类中哪个构造函数,同时子类构造函数中如果使用了this调
用了本类构造函数时,那么super就没有了,因为super和this都只能定义
在第一行 ,所以只能有一个,但是可以保证的是,子类中肯定会有其
他的构造函数访问父类的构造函数。
注意:super语句必须要定义在子类中的第一行,因为父类的初始化动作要先完成
递归
递归结构包括两个部分:
-
递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。
-
递归体:什么时候需要调用自身方法。
封装
定 义
类是属性和方法的集合,开发人员不需要了解类中每句代码的含义,只需要通过对象来调用类内某个属性或方法即可实现某项功能,这就是类的封装性。将属性和方法私有化,提供具体访问的方式。
定 义:隐藏对象属性 ,实现细节,仅对外界提供访问方式。
封装原则
将不需要对外界提供的内容都隐藏起来把属性隐藏,提供公共方案对其访问。
关键字private
private : 私有是一个权限种体现——父类中使用private的方法不能被子类继承
封装好处 :隔离变化 便于使用 提高重用性 提高安全性
封装原则 :隐藏属性 对外提供公修饰符,用于修饰成员,被私有的成员只在本类中有效 +(方法中对数据进行逻辑判断)set get
私有仅仅是封装的一种方法。
函数是最小的封装体。
装箱和拆箱
把基本数据类型封装成对象,也成为装箱。
把基本数据类型的封装类的对象转换成基本数据类型的变量,也成为拆箱
由于这种装箱和拆箱的过程非常繁琐,在Java5之后装箱和拆箱的过程可以自动完成,也成为自动拆装箱。
把基本数据类型的数值封装成对象,也成为自动装箱
把封装类的对象拆箱成基本数据类型的数值,也成为自动拆箱
继承
定义
通过继承可以创建子类和父类之间的层次关系,子类可以从父类中继承属性和方法,通过这种关系模式简化类的操作。先定义A在定义B,B通过extends关键字继承A,提高了程序的开发效率。
好 处
提高代码复用性,让类与类之间产生类关系,给第三个特征多态提供类前提。
关键字extends :继承
单 继 承:一个子类只能有一个直接父类,好处是减少了方法的混用。
多 继 承: 一个子类可以有多个直接父类(java中不允许,进行改良)不直接支持,因为多个父类中有相同成员,会产生调用的不确定性,java中通过“多实现”的方式来体现。好处是极大的提高了代码的复用性。坏处:多重继承能导致方法调用的冲突。
多重继承
java支持
C继承B,B继承A
使用继承:
1,查看该体系中的顶层类,了解该体系的基本功能
2,创建体系中的最子类对象,完成功能的使用。
何 时 用
当类与类之间存在所属关系的时候,就定义继承。
成员特点:当本类的成员和局部变量同名用
1,成员变量:this区分
当子类中的成员变量同名用super区分父类
this和super的用法很相似
this :代表一个本类对象的引用
super:代表一个父类空间 (因为内存中没有父类对象 )
子类不能直接访问父类私有变量,可通过方法间接访问:如果父类中有私有属性的set和get方法,而且是public修饰,子类继承父类的同时,也继承了带有public修饰的set和get方法,就可以访问。
2,成员函数
当子父类中出现 成员函数一模一样的情况,会运行子类的函数,这种现象,称为覆盖操作,这是函数在子父类中的特性。
方法继承的规则
-
父类中声明为 public 的方法在子类中也必须为 public。
-
父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
-
父类中声明为 private 的方法,不能够被子类继承。
多态
多态性
方法的覆盖、重载与动态绑定构成了多态性。
多态存在的必要条件:
-
要有继承或实现;
-
要有重写;
-
父类引用指向子类对象;(向上转型)
多态优点:
-
简化代码;
-
改善代码的组织性和可读性;
-
易于扩张;
可变参数
-
在方法声明中,在指定参数类型后加省略号(...)
-
在一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在他之前声明。
public static void printMax(double... numbers){ if(numbers.length == 0){ System.out.println("没有值传进来"); return; } double result = numbers[0]; //排序 for(int i = 1;i< numbers.length;i++){ if(numbers[i] > result){ result = numbers[i]; } } System.out.println("最大值为:"+result); }
重载
重载在同一个类中:同名不同参,与返回值无关。
覆盖
子类中,覆盖也称为重写、覆写。
覆盖注意事项
1,子类方法覆盖父类方法时,子类权限必须要大于等于父类的权限
2,静态只能覆盖静态,或被静态覆盖。
何时用覆盖
当对一个类进行子类的扩展时,子类需要保留父类的功能声明,但是要定义子类中该功能的特有内容时。
多态的转型
-
向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。
使用格式:
父类类型 变量名 = new 子类类型();
例如:Person p = new Student();
-
向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。如果是直接创建父类对象,是无法向下转型的。
使用格式:
子类类型 变量名 = (子类类型) 父类类型的变量;
例如:Student stu = (Student) p; //变量p 实际上指向Student对象
注意事项
-
向上转型后子类不能调用自己的私有属性和私有方法,只能调用父类属性和重写父类方法
-
如果向上转型后想要调用子类的私有属性和方法,可以强制向下转型
Animal t1=(Animal)t 新创建了一个Animal类型的对象t1指向强制转换类型后的t 注:**t1和t的地址一样**
-
向下转型是有风险的,必须是继承关系才能转型成功,否则程序运行时就会抛出异常,为了防止这种情况,就要使用instanceof运算符(代码看链接2,作者写的通俗易懂)
instanceof
用于判断对象的具体类型、接口 ( if(对象 instanceof 类) );
原文链接:1. 多态的向上转型与向下转型_云飞扬°的博客-CSDN博客_多态向上转型和向下转型 AND
对象实例过程
Person P=new Person( );
1,JVM会读取指定的路径下的 Person.class文件,并加载进内存,并会先加载Person的父类(如果有直接到父类的情况下)。
2,在堆内存中开盘空间,分配地址。
3,并在对象空间中,对对象中的属性进行默认初始化。
4,调用对应的构造函数进行初始化。
5,在构造函数中,第一行会先调用父类中构造函数进行初始化。
6,父类初始化完毕后,再对子类的属性进行显示初始化。
7,再进行子类构造函数的特定初始化。
8,初始化完毕后,将地址赋值给引用变量。
抽象类和接口
抽象类
特点
1,方法只有声明没有实现时,该方法就是抽像方法,需要被abstract修饰,抽象方法必须定义在抽象类中,该类必须也被abstract 修饰
2,抽象类不可以被实例化,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
3,抽象类必须有其子类覆盖类所有的抽象方法后,该子类才可以实例化,否则,这个子类还是抽象类
示例
abstract class Test{ int myint; public abstract void noAction(); //这是抽象方法,没有方法体 public int getMyint(){ return myint; //这是非抽象方法,包含方法体 } } //继承抽象类 class testClass extends Test{ public void noAction(){ System.out.println("this is noAction"); //实现抽象方法,定义了方法体 } }
疑问
1,抽象类中有构造函数吗?
有,用于给子类对象进行初始化
2,抽象类可以不定义抽象方法吗?
可以,但是很少见,目的就是不让该类创建对象,ANT的适配器对象就是这种类,通常这个类中的方法有方法体,但是却
没内容(有大括号,里面没内容)
3,抽象关键字不可以和哪些关键字共存?
private :抽象需要覆盖,私有会隐藏起来
static :加静态直接用类名调用,但抽象方法运行没有意义
final :抽象类需要被覆盖,而final不能被覆盖
4,抽象类与一般类的异同点
相同点:
都是用来描述事物的,都在内部定义了成员
不同点:
1,一般类有足够的信息描述事物
抽象类描述事物的信息有可能不足
2,一般类中不能定义抽象方法,只能定义非抽象方法
抽象类中科定义抽象方法,同时也可以定义非抽象方法
3,一般类可以被实例化
抽象类不可以被实例化
5,抽象类一定是个父类吗
是的,因为需要子类覆盖其方法后才可以对子类实例化
接口
定义
接口是一种用于描述类对外提供功能规范的、能够多重继承的、特殊的抽象类。Java不支持多继承,所以将多继承机制改良,
变成了接口的实现形式。接口中只能定义静态常量和抽象方法。
为什么定义接口:主要是某些现实问题需要用多重继承描述,但又不适合放在父类中。
示例
public interface IA{ //接口IA public abstract int A1(); //抽象方法 public int A2(); //接口中,不使用abstract声明的方法也是抽象方法 }
特点
1,接口中成员有固定修饰符
2,接口中成员都是公共的的权限
3,接口不可以实例化
只能由实现了接口的子类并覆盖了接口中所有的抽象方法后,该子类才可以实例化否则,这个子类是抽象类
4,实现接口必须有public
5,接口与接口之间是继承关系,而且接口可以多继承
6,接口是对外暴露的规则
7,接口是程序的功能扩展
8,接口的出现降低耦合性
接口与类相似点:
-
一个接口可以有多个方法。
-
接口文件保存在 .java 结尾的文件中,文件名使用接口名。
-
接口的字节码文件保存在 .class 结尾的文件中。
-
接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
-
接口不能用于实例化对象。
-
接口没有构造方法。
-
接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
-
接口不能包含成员变量,除了 static 和 final 变量。
-
接口不是被类继承了,而是要被类实现。
-
接口支持多继承。
接口特性
-
接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
-
接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
-
接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别
相同点:
都是不断向上抽取而来的
不同点:
1,抽象类需要被继承,而且只能单继承
接口需要被实现,而他却可以多实现
2,抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法
接口中只能定义抽象方法,必须由子类去实现
3,抽象的继承,在定义该体系的基本共性内
接口的实现,在定义体系额外功能
-
抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
-
抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
-
接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
-
一个类只能继承一个抽象类,而一个类却可以实现多个接口。
异常
Java的三种异常
-
检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
-
运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
-
错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
Exception 类
所有的异常类是从 java.lang.Exception 类继承的子类。
Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。
Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
Error 用来指示运行时环境发生的错误。
例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
异常类有两个主要的子类:IOException 类和 RuntimeException 类。
异常的处理方式
捕获异常: 对异常进行捕获然后进行指定方式的处理
抛出异常:
创建异常对象,封装异常信息然后通过throw将异常对象传递给调用者。 不对异常进行处理只对异常进行抛出是非常不负责任的表现可以称为渣男。 但是可以通过主动抛出异常,对一些jvm虚拟机识别不出来的异常进行抛出。
throw与throws的区别: 1.抛出的东西不同:throw抛出的是具体的异常对象,而throws抛出的是抽象的异常类。 2.使用位置不同:throw一般用在方法体中,也可用在代码块中,throws只能用在方法声明括号后面。
数组
定义
数组是用来存储相同数据类型的数据集合,可以看成是多个相同类型数据的组合,实现对这些数据的统一管理。
数组的引用存在栈中,而数组中的元素都存储在堆中。数组作为一种容器,它在堆中所被分配的是一段连续的、大小相同空间。用来存放元素,这段连续的空间每一个空间都有下标,下标是连续的对应的
连续:内存地址是连续的。如a是首地址,a+1就是第二个数据元素的地址,a+2是第三个。。。 大小相同:指每个数组元素所占的空间大小是相同的。((a+i)-(a+i-1)=定值 是多少?) 如: int a[]={1,2,3,4}; 示例: a a+1 a+2 a+3 1 2 3 4 a[0] a[1] a[2] a[3] 注意:数组名不能被赋值,因为它是个常量值。代表数组的首地址。
声明和初始化
声明
-
int[] a; //首选
-
int a[]; //效果相同,声明风格源自于C/C++
初始化
-
int[] a = new int[5];
-
int[] a = {1,2,3,4,5};
遍历
利用 length(数组的长度) 遍历数组
int[] a = new int[10];
a.length的值就为10;
-
数组的长度必须>=0
-
length为只读
-
利用length遍历数组
数组的优缺点
优点:
数组作为一种元素容器,它可以存储任意类型的元素,因为可以定义成Object[] 因为它在内存中的存储方式是连续空间,每一个空间下面都有一个下标,所以数组的读取速度是非常的快的,假设要读取arr[9]的数据,那么只需要将数组下标的指针移动9+1个位置,然后就可以找到对应的数据,这对计算机来说是非常快的。
缺点:
数组在创建时在堆中分配空间时长度就已经给定了,它不能做扩展!!这也导致了它的机动性大大的降低,为什么不能做扩展?因为数组空间的前面的空间和后面的空间也可能存储着其他的数据,数组要扩展的话只能找别的空间,从而无法保证自己空间连续的特性。
缺点的改进:
Arrays类中提供了这样的一个静态方法copyOf();
这个方法在堆里开辟了一个新的空间,数组长度定义的更大,然后把原来数组的元素全都复制过来,这样就产生了一个新的数组。
然后我们再把原数组的引用指向新的数组,这样就实现了数组的扩容了。
集合类ArrayList的数据存储方式就是数组,这个集合的特点就是有序,动态扩展,值可重复,其中它的可扩展性就是通过我上述的这个方法来实现的。 那么也可以看出来这个集合的读取速度是非常快,但是扩展速度是非常的慢的,每一次添加数据他都需要复制一次,如果数据量庞大的话,那就非常的慢了。
原文链接:java数组的存储方式_但你没有#的博客-CSDN博客_java数组存储方式
数组异常
空指针异常:没有创建而直接使用数组时会产生空指针异常,类名为:java.lang.NullPointerException
数组下标越界异常:访问数组时的下标超出数组的最大下标时产生该异常,类名为: java.lang.ArrayIndexOutOfBoundsException
数组的复制
Java中的数组的复制使用 System.arraycopy 方法可以实现,带参如下:
System.arraycopy( source, srcPos, dest, destPos, length);
参数含义如下:
source:原数组;
srcPos:原数组中的起始位置;
dest: 目标数组;
destPos:目标数组中的起始位置;
length:要复制的数组元素的个数;
Arrays类
java.util.Arrays类是JDK提供的一个工具类,用来处理数组的各种方法,而且每个方法基本上都是静态方法,能够直接通过类名Arrays调用。
Arrays.sort(数组名)方法用于数组排序,在Arrays类中有该方法的一系列重载方法,能对byte、char、double、float、int、long、short进行排序。
Arrays.sort( arr_name)
Arrays.sort( arr_name, fromIndex, toIndex)
其中:
arr_name:数组名称;
fromIndex:排序的起始下标;
toIndex: 排序的终止下标;
二维数组
声明格式:
-
int a[][]
-
int[][] b
-
int[] c[]
创建格式:
数组名 = new 数据元素类型[行数][列数]; 数组名 = new 数据元素类型[行数][ ];
不合法:
a = new int[][4];
必须声明行个数
声明直接初始化
int a = {{1,2,3}, {4,5,6}, {7,8,9}};
冒泡排序
-
比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置。
-
每一次比较,都会产生出一个最大或者最小的数。
-
下一轮则可以少一次排序。
-
依次循环直到结束。
public static int[] sort(int[] array){ //临时变量 int temp = 0; //外层循环,判断我们这个要走多少次 for(int i = 0; j < array.length-1;i++){ //内层循环,比较判断两个数,如果第一个数比第二个数大,则交换位置 for(int j = 0; j < array.length-1-i; j++){ if(array[j+1]>array[j]){ temp = arra[j]; array[j] = array[j+1]; array[j+1] = temp; } } } return array; }
时间复杂度为O(n^2)
稀疏数组
集合
概述
Collection表示集合,当需要管理多个对象的时候可以使用Collection中的类,Collection框架分三层,
第一层是接口,第二层是抽象类,第三层是实际要使用的类。
序号 | 接口描述 |
---|---|
1 | Collection 接口 Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。Collection 接口存储一组不唯一,无序的对象。 |
2 | List 接口 List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。List 接口存储一组不唯一,有序(插入顺序)的对象。 |
3 | Set Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。Set 接口存储一组唯一,无序的对象。 |
4 | SortedSet 继承于Set保存有序的集合。 |
5 | Map Map 接口存储一组键值对象,提供key(键)到value(值)的映射。 |
6 | Map.Entry 描述在一个Map中的一个元素(键/值对)。是一个 Map 的内部接口。 |
7 | SortedMap 继承于 Map,使 Key 保持在升序排列。 |
8 | Enumeration 这是一个传统的接口和定义的方法,通过它可以枚举(一次获得一个)对象集合中的元素。这个传统接口已被迭代器取代。 |
Collection接口
集合框架中最顶层的接口就是Collection,表示一个集合。因为是接口,所以主要考虑它的方法,这个接口中定义的方法是所有实现该接口的类都应该实现的。Collection的方法都是与集合操作相关的方法。
Set和List的区别
-
Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
-
Set 检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。
-
List 和数组类似,可以动态增长,根据实际存储的数据的长度自动增长 List 的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector> 。
ArrayList
概述
1.ArrayList是基于数组实现的,是动态数组,容量能自动增长;ArrayList 继承了 AbstractList ,并实现了 List 接口。与普通数组的区别就是它是没有固定大小的限制;
2.ArrayList线程不安全,只能在单线程下使用。多线程环境下使用Collections.synchronizedList(List)返回一个线程安全的ArryayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类;
3 .ArrayList实现了Serializable接口,因此支持序列化传输;ArrayList实现了RandomAccess接口,因此支持通过下标序号进行快速访问;ArrayList实现了Cloneable接口,因此支持被克隆;
4.通过ensureCapacity增加ArrayList类的容量;
5.无参构造方法构造的容量默认为10,带有Collection参数的构造方法,将collection转化为数组赋给ArrayList的实现数组elementData;
6.ArrayList实现中调用了Arrays.copyof()方法,该方法就是在内部创建一个长度为newlength的数组;调用System.arraycopy方法,该方法被标记了native,可以保证数组内元素的正确复制和移动,适合批量处理数组,效率较高;
7.ArrayList基于数组实现,通过下标索引查找指定位置的元素,因此查询效率高;插入和删除效率低;
8.ArrayList将元素的值分为null和不为null两种情况处理,支持null存储;
9.ArrayList是有序的,可以包含重复数据;
原文链接:java中HashMap、ArrayList、HashSet的存储原理及遍历方式汇总_孤独天狼的博客-CSDN博客_arraylist存储方式
声明及操作
import java.util.ArrayList; // 引入 ArrayList 类 ArrayList<E> objectName =new ArrayList<>(); // 初始化
-
E: 泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型。
-
objectName: 对象名。
ArrayList 是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
添加add(" "):
ArrayList<String> sites = new ArrayList<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Weibo"); System.out.println(sites);
get(下标)访问数据:
ArrayList<String> sites = new ArrayList<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Weibo"); System.out.println(sites.get(1)); // 访问第二个元素
set(下标位置, "修改数值")修改
ArrayList<String> sites = new ArrayList<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Weibo"); sites.set(2, "Wiki"); // 第一个参数为索引位置,第二个为要修改的值 System.out.println(sites);
remove(下标)删除元素
ArrayList<String> sites = new ArrayList<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Weibo"); sites.remove(3); // 删除第四个元素 System.out.println(sites);
size()计算大小
ArrayList<String> sites = new ArrayList<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Weibo"); System.out.println(sites.size()); //输出结果为4
迭代数组列表
//方式一 ArrayList<String> sites = new ArrayList<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Weibo"); for (int i = 0; i < sites.size(); i++) { System.out.println(sites.get(i)); } //方式二 ArrayList<String> sites = new ArrayList<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Weibo"); for (String i : sites) { System.out.println(i); }
其他的引用类型
ArrayList<E> 中的元素实际上是对象,在以上实例中,数组列表元素都是字符串 String 类型。
如果我们要存储其他类型,而 <E> 只能为引用数据类型,这时我们就需要使用到基本类型的包装类。
排序
ArrayList<Integer> myNumbers = new ArrayList<Integer>(); myNumbers.add(33); myNumbers.add(15); myNumbers.add(20); myNumbers.add(34); myNumbers.add(8); myNumbers.add(12); Collections.sort(myNumbers); // 数字排序 for (int i : myNumbers) { System.out.println(i); }
ArrayList常用方法
方法 | 描述 |
---|---|
add() | 将元素插入到指定位置的 arraylist 中 |
addAll() | 添加集合中的所有元素到 arraylist 中 |
clear() | 删除 arraylist 中的所有元素 |
clone() | 复制一份 arraylist |
contains() | 判断元素是否在 arraylist |
get() | 通过索引值获取 arraylist 中的元素 |
indexOf() | 返回 arraylist 中元素的索引值 |
removeAll() | 删除存在于指定集合中的 arraylist 里的所有元素 |
remove() | 删除 arraylist 里的单个元素 |
size() | 返回 arraylist 里元素数量 |
isEmpty() | 判断 arraylist 是否为空 |
subList() | 截取部分 arraylist 的元素 |
set() | 替换 arraylist 中指定索引的元素 |
sort() | 对 arraylist 元素进行排序 |
toArray() | 将 arraylist 转换为数组 |
toString() | 将 arraylist 转换为字符串 |
ensureCapacity() | 设置指定容量大小的 arraylist |
lastIndexOf() | 返回指定元素在 arraylist 中最后一次出现的位置 |
retainAll() | 保留 arraylist 中在指定集合中也存在的那些元素 |
containsAll() | 查看 arraylist 是否包含指定集合中的所有元素 |
trimToSize() | 将 arraylist 中的容量调整为数组中的元素个数 |
removeRange() | 删除 arraylist 中指定索引之间存在的元素 |
replaceAll() | 将给定的操作内容替换掉数组中每一个元素 |
removeIf() | 删除所有满足特定条件的 arraylist 元素 |
forEach() | 遍历 arraylist 中每一个元素并执行特定操作 |
LinkedList
概述
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。
链表可分为单向链表和双向链表。
一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接。
一个双向链表有三个整数值: 数值、向后的节点链接、向前的节点链接。
Java LinkedList(链表) 类似于 ArrayList,是一种常用的数据容器。
与 ArrayList 相比,LinkedList 的增加和删除的操作效率更高,而查找和修改的操作效率较低。
以下情况使用 ArrayList :
-
频繁访问列表中的某一个元素。
-
只需要在列表末尾进行添加和删除元素操作。
以下情况使用 LinkedList :
-
你需要通过循环迭代来访问列表中的某些元素。
-
需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
声明及操作
// 引入 LinkedList 类 import java.util.LinkedList; LinkedList<E> list = new LinkedList<E>(); // 普通创建方法 或者 LinkedList<E> list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表
LinkedList<String> sites = new LinkedList<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Weibo"); //通过add("")添加元素 //更多的情况下我们使用 ArrayList 访问列表中的随机元素更加高效,但以下几种情况 LinkedList 提供了更高效的方法。 // 使用 addFirst() 在头部添加元素 sites.addFirst("Wiki"); // 使用 addLast() 在尾部添加元素 sites.addLast("Wiki"); // 使用 removeFirst() 移除头部元素 sites.removeFirst(); // 使用 removeLast() 移除尾部元素 sites.removeLast(); // 使用 getFirst() 获取头部元素 System.out.println(sites.getFirst()); // 使用 getLast() 获取尾部元素 System.out.println(sites.getLast()); //使用 for 配合 size() 方法来迭代列表中的元素 for (int size = sites.size(), i = 0; i < size; i++) { System.out.println(sites.get(i)); } //使用增强for迭代列表中的元素 for (String i : sites) { System.out.println(i); }
LinkedList常用方法
方法 | 描述 |
---|---|
public boolean add(E e) | 链表末尾添加元素,返回是否成功,成功为 true,失败为 false。 |
public void add(int index, E element) | 向指定位置插入元素。 |
public boolean addAll(Collection c) | 将一个集合的所有元素添加到链表后面,返回是否成功,成功为 true,失败为 false。 |
public boolean addAll(int index, Collection c) | 将一个集合的所有元素添加到链表的指定位置后面,返回是否成功,成功为 true,失败为 false。 |
public void addFirst(E e) | 元素添加到头部。 |
public void addLast(E e) | 元素添加到尾部。 |
public boolean offer(E e) | 向链表末尾添加元素,返回是否成功,成功为 true,失败为 false。 |
public boolean offerFirst(E e) | 头部插入元素,返回是否成功,成功为 true,失败为 false。 |
public boolean offerLast(E e) | 尾部插入元素,返回是否成功,成功为 true,失败为 false。 |
public void clear() | 清空链表。 |
public E removeFirst() | 删除并返回第一个元素。 |
public E removeLast() | 删除并返回最后一个元素。 |
public boolean remove(Object o) | 删除某一元素,返回是否成功,成功为 true,失败为 false。 |
public E remove(int index) | 删除指定位置的元素。 |
public E poll() | 删除并返回第一个元素。 |
public E remove() | 删除并返回第一个元素。 |
public boolean contains(Object o) | 判断是否含有某一元素。 |
public E get(int index) | 返回指定位置的元素。 |
public E getFirst() | 返回第一个元素。 |
public E getLast() | 返回最后一个元素。 |
public int indexOf(Object o) | 查找指定元素从前往后第一次出现的索引。 |
public int lastIndexOf(Object o) | 查找指定元素最后一次出现的索引。 |
public E peek() | 返回第一个元素。 |
public E element() | 返回第一个元素。 |
public E peekFirst() | 返回头部元素。 |
public E peekLast() | 返回尾部元素。 |
public E set(int index, E element) | 设置指定位置的元素。 |
public Object clone() | 克隆该列表。 |
public Iterator descendingIterator() | 返回倒序迭代器。 |
public int size() | 返回链表元素个数。 |
public ListIterator listIterator(int index) | 返回从指定位置开始到末尾的迭代器。 |
public Object[] toArray() | 返回一个由链表元素组成的数组。 |
public T[] toArray(T[] a) | 返回一个由链表元素转换类型而成的数组。 |
ArrayList和LinkedList区别
原文链接:ArrayList和LinkedList区别?看完秒懂~_程序员十一郎的博客-CSDN博客_arraylist linkedlist
HashMap
概述
-
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射,HashMap在JDK1.7之前底层是用数组+链表的方式存储的;在JDK1.8之后底层是用数组+链表+红黑树存储的;
-
HashMap里面实现了一个静态内部类Entry;
-
Entry的属性有key,value,next,hash。Entry存储的是key-value键值对;
-
HashMap存储是无序的,即不会记录插入的顺序。
-
HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。
-
HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。
声明及操作
HashMap 的 key 与 value 类型可以相同也可以不同,可以是字符串(String)类型的 key 和 value,也可以是整型(Integer)的 key 和字符串(String)类型的 value。
Map<String,String> map = Map.of("goole","goole.com","baidu","baidu.com");
Key | Value |
---|---|
goole | goole.com |
baidu | baidu.com |
Map<Integer,String> map = Map.of(1,"goole",2,"baidu");
Key | Value |
---|---|
1 | goole |
2 | baidu |
HashMap 中的元素实际上是对象,一些常见的基本类型可以使用它的包装类。
基本类型对应的包装类表如下:
基本类型 | 引用类型 |
---|---|
boolean | Boolean |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
// 创建 HashMap 对象 Sites HashMap<Integer, String> Sites = new HashMap<Integer, String>(); // 添加键值对 Sites.put(1, "Google"); Sites.put(2, "Runoob"); Sites.put(3, "Taobao"); Sites.put(4, "Zhihu"); //通过get(key)访问元素 System.out.println(Sites.get(3)); //通过remove(key)删除元素 Sites.remove(4); //删除所有键值对(key-value)可以使用 clear 方法 Sites.clear(); //通过size()方法计算hashmap大小 System.out.println(Sites.size()); //如果你只想获取 key,可以使用 keySet() 方法,然后可以通过 get(key) 获取对应的 value,如果你只想获取 value,可以使用 values() 方法。 //第一种 for-each 来迭代 HashMap 中的元素。 // 输出 key 和 value for (Integer i : Sites.keySet()) { //输出key+value System.out.println("key: " + i + " value: " + Sites.get(i)); } // 返回所有 value 值 for(String value: Sites.values()) { // 输出每一个value System.out.print(value + ", "); } //第二种 "通过Map.entrySet使用iterator遍历key和value:" Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, String> entry = it.next(); System.out.println("获取Map中key的值 " + entry.getKey()); System.out.println("获取value的值 " + entry.getValue()); } //第三种:使用比较多,推荐使用,尤其是容量大时 for (Map.Entry<String, String> entry : map.entrySet()) { System.out.println("获取Map中key的值" + entry.getKey()); System.out.println("获取value的值" + entry.getValue()); } //第四种:使用很少,不推荐 "通过Map.values()遍历所有的value,但不能遍历key" for (String v : map.values()) { System.out.println("value= " + v); }
[java] Map循环遍历的5种方法实现_肖坤的博客-CSDN博客
HashMap常用方法
方法 | 描述 |
---|---|
clear() | 删除 hashMap 中的所有键/值对 |
clone() | 复制一份 hashMap |
isEmpty() | 判断 hashMap 是否为空 |
size() | 计算 hashMap 中键/值对的数量 |
put() | 将键/值对添加到 hashMap 中 |
putAll() | 将所有键/值对添加到 hashMap 中 |
putIfAbsent() | 如果 hashMap 中不存在指定的键,则将指定的键/值对插入到 hashMap 中。 |
remove() | 删除 hashMap 中指定键 key 的映射关系 |
containsKey() | 检查 hashMap 中是否存在指定的 key 对应的映射关系。 |
containsValue() | 检查 hashMap 中是否存在指定的 value 对应的映射关系。 |
replace() | 替换 hashMap 中是指定的 key 对应的 value。 |
replaceAll() | 将 hashMap 中的所有映射关系替换成给定的函数所执行的结果。 |
get() | 获取指定 key 对应对 value |
getOrDefault() | 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值 |
forEach() | 对 hashMap 中的每个映射执行指定的操作。 |
entrySet() | 返回 hashMap 中所有映射项的集合集合视图。 |
keySet() | 返回 hashMap 中所有 key 组成的集合视图。 |
values() | 返回 hashMap 中存在的所有 value 值。 |
merge() | 添加键值对到 hashMap 中 |
compute() | 对 hashMap 中指定 key 的值进行重新计算 |
computeIfAbsent() | 对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hasMap 中 |
computeIfPresent() | 对 hashMap 中指定 key 的值进行重新计算,前提是该 key 存在于 hashMap 中。 |
HashSet
概述
HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
HashSet 允许有 null 值。
HashSet 是无序的,即不会记录插入的顺序。
HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 您必须在多线程访问时显式同步对 HashSet 的并发访问。
HashSet 实现了 Set 接口。
HashSet不是不同步的,需要外部保持线程之间的同步问题;
HashSet存储值,必须覆盖hashCode()方法和equals()方法
声明及操作
HashSet<String> sites = new HashSet<String>(); sites.add("Google"); sites.add("Runoob"); sites.add("Taobao"); sites.add("Zhihu"); sites.add("Runoob"); // 重复的元素不会被添加 System.out.println(sites); //contains() 方法来判断元素是否存在于集合当中 System.out.println(sites.contains("Taobao")); //remove删除集合中的元素 sites.remove("Taobao"); // 删除元素,删除成功返回 true,否则为 false //clear()清除集合中所有的元素 sites.clear(); //size() 方法计算集合中元素的数量 System.out.println(sites.size()); //for-each迭代集合中的元素 for (String i : sites) { System.out.println(i); }
存储原理及遍历方式
原文链接:java中HashMap、ArrayList、HashSet的存储原理及遍历方式汇总_孤独天狼的博客-CSDN博客_arraylist存储方式