目录
1. 什么是集合框架
2. 什么是数据结构
3. 容器背后对应的数据结构
4. 相关java知识
5. 时间复杂度
6. 空间复杂度
7. 包装类
7.1 装箱和拆箱
7.2 阿里面试题:
8. 泛型
8.1 泛型的语法
8.2 泛型怎样编译
9. 泛型的上界
9.1 语法
9.2 泛型方法
1. 什么是集合框架
Java集合框架又被称为container,是定义在java.util包下的一组接口interfaces和其实现类classes.
主要表现为将多个元素element置于一个单元中,用于对这些元素进行快速、便捷的存储store、检索retrieve、管理manipulate,即我们俗称的增删改查CEUD
2. 什么是数据结构
数据结构是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合
3. 容器背后对应的数据结构
每个容器都是对某种特定数据结构的封装
Collection:是一个接口,包含大部分容器常用的一些方法
List:是一个接口,规范了ArrayList和LinkedList中要实现的方法
- ArrayList:实现了List接口,底层为动态类型顺序表
- LinkedList:实现了List接口,底层为双向链表
Stack:底层是栈,栈是一种特殊的顺序表
Queue:底层是队列,队列是一种特殊的顺序表
Deque:是一个接口
Set:集合,是一个接口,里面放置的是K模型
- HashSet:底层为哈希桶
- TreeSet:底层为红黑树
Map:映射,里面存储的是K-V模型的键值对
- HashMap:底层为哈希桶
- TreeMap:底层为红黑树
4. 相关java知识
- 泛型Generic
- 自动装箱autobox和自动拆箱autounbox
- Object的equals方法
- Comparable和Comparator接口
5. 时间复杂度
大O符号:是用于描述函数渐进行为的数学符号
推导大O阶方法:
- 用常数1取代运行时间中的所有加法常数
- 在修改后的运行次数函数中,只保留最高阶项
- 如果最高阶项存在且不是1,则去除与这个项目相乘的常数
平常所说的时间复杂度和空间复杂度都是指最坏情况下
注意:结合算法思想,不能只看代码
冒泡排序的时间复杂度:O (n^2)(一开始就是倒序的),最好是O(n)(一开始就是有序的)
二分查找的时间复杂度:O(logn)
阶乘递归的时间复杂度:O(n)
斐波那契递归的时间复杂度:O(2^n)
6. 空间复杂度
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。空间复杂度不是程序占用了多少bytes的空间,而是变量的个数。与时间复杂度一样,也使用大O渐进表示法
冒泡排序的空间复杂度:O(1)
阶乘递归的空间复杂度:O(n)
常遇到的复杂度:
O(1)<O(logN)<O(N)<O(N*logN)<O(N^2)
7. 包装类
在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型
7.1 装箱和拆箱
装箱:把基本数据类型变为包装类类型的过程
public class Test {
public static void main(String[] args) {
int a = 10;
Integer i = Integer.valueOf(a);//显式装箱
Integer i2 = 10;//自动装箱(隐式装箱,底层调用了Integer.valueOf方法)
Double d = Double.valueOf(a);
}
}
拆箱:把包装类类型变为基本数据类型的过程
public class Test {
public static void main(String[] args) {
Integer a = 10;
int b = a;//自动拆箱
int c = a.intValue();//显式、手动
double d = a.doubleValue();
}
}
使用javap -c在cmd中(找到该段代码的文件位置然后输入cmd)可以查看底层代码实现
7.2 阿里面试题:
public class Test {
public static void main(String[] args) {
Integer a = 100;
Integer b = 100;
System.out.println(a == b);//true
Integer c = 200;
Integer d = 200;
System.out.println(c == d);//false
}
}
分析:
装箱的操作:
@IntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
上述代码中的 i 应该在一个范围的时候直接返回数组中的值,否则返回新的对象
用等号比较,必然不一样。
i 的范围:-128~127(共256个数字,在cache缓存中)
8. 泛型
一般的类和方法,只能使用具体的类型:基本类型或自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。——《Java编程思想》
泛型,通俗讲:就是适用于许多许多类型;从代码上将:对类型实现了参数化
class MyArray{
public Object[] array=new Object[10];
public void setValue(int index,Object value){
array[index]=value;
}
public Object getValue(int index){
return array[index];
}
}
public class Test {
public static void main(String[] args) {
MyArray myArray=new MyArray();
myArray.setValue(0,10);
myArray.setValue(1,"hello");
String str=(String)myArray.getValue(1);
System.out.println(str);//hello
}
}
以上代码存在一些问题:
- 存放数据太乱,什么类型都能放
- 每次获取数据时,都要进行强转
泛型可以解决,泛型的主要目的:指定当前的容器,要持有什么类型的对象,让编译器去检查。此时,要把类型作为参数传递,需要什么类型,就传入什么类型。
8.1 泛型的语法
class 泛型类名称<类型形参列表>{
//这里可以使用类型参数
}
class MyArray<E>{//<E>占位符表示一个泛型
public Object[] array=new Object[10];
public void setValue(int index,E value){
array[index]=value;
}
public E getValue(int index){
return (E)array[index];
}
}
public class Test {
public static void main(String[] args) {
MyArray<Integer> myArray=new MyArray<Integer>();
myArray.setValue(0,10);
// myArray.setValue(1,"hello");//自动类型检查
Integer value=myArray.getValue(0);//自动类的转换
System.out.println(value);//10
MyArray<String> myarray=new MyArray<>();//可以省略类型实参的填写
myarray.setValue(0,"hello");
String value1=myarray.getValue(0);
System.out.println(value1);//hello
}
}
了解:【规范】类型形参一般使用一个大写字母表示,常用名称有:
E表示Element,K表示Key,V表示Value,N表示Number,T表示Type
<>中只能写类类型,不能写简单类型(编译不能通过)
裸类型是一个泛型类但没有带着类型实参,例如:
MyArray list=new MyArray();
注意:我们不要自己去使用裸类型,裸类型是为了兼容老版本的API保留的机制
8.2 泛型怎样编译
泛型是编译时期的一种机制,在运行的时候没有泛型的概念【JVM中没有泛型的概念】
在编译的过程中,将所有的E替换为Object这种机制,称之为:擦除机制
class MyArray<E> {//<E>占位符表示一个泛型
public Object[] array = new Object[10];
public void setValue(int index, E value) {
array[index] = value;
}
public E getValue(int index) {
return (E) array[index];
}
}
public class Test {
public static void main(String[] args) {
MyArray<Integer> myArray = new MyArray<>();
MyArray<Integer> myArray2 = new MyArray<>();
Test test = new Test();
System.out.println(myArray);//MyArray@3b07d329
System.out.println(myArray2);//MyArray@41629346
System.out.println(test);//Test@404b9385
}
}
9. 泛型的上界
在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束
9.1 语法
class 泛型类名称<类型形参 extends 类型边界>{
...
}
例如:
public class MyArray<E extends Number>{
...
}
没有指定类型边界的E,可以视为E extends Object
复杂实例:
public class MyArray<E extends Comparable<E>>{
...
}
E必须是实现了Comparable接口的
class Alg<E extends Comparable<E>> {
public E findMax(E[] arr) {
E max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i].compareTo(max) > 0) {
max = arr[i];
}
}
return max;
}
}
class Person implements Comparable<Person> {
@Override
public int compareTo(Person o) {
return 0;
}
}
public class Test {
public static void main(String[] args) {
Alg<Integer> alg = new Alg<>();
System.out.println(alg.findMax(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}));//10
Alg<Person> alg1 = new Alg<>();
System.out.println(alg1.findMax(new Person[]{new Person()}));//Person@41629346
}
}
9.2 泛型方法
class Alg {
public<E extends Comparable<E>> E findMax(E[] arr) {
E max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i].compareTo(max) > 0) {
max = arr[i];
}
}
return max;
}
}
public class Test {
public static void main(String[] args) {
Alg alg = new Alg();
Integer[] arr = {1,2,3,4,5,6};
int ret=alg.findMax(arr);
System.out.println(ret);//6
}
}
静态泛型方法:
class Alg {
public static <E extends Comparable<E>> E findMax(E[] arr) {
E max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i].compareTo(max) > 0) {
max = arr[i];
}
}
return max;
}
}
public class Test {
public static void main(String[] args) {
Integer[] arr = {1, 2, 3, 4, 5, 6};
System.out.println(Alg.findMax(arr));//6
}
}