week06_day05_自己实现ArrayList
Collection是一个跟接口吗? 不是,它还继承自Iterable接口
泛型擦除也就是泛型被诟病的一个原因,因为在jvm执行的时候,它里面根本就没有泛型的信息。
package com.cskaoyan.jdk5;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
泛型擦除:编译器在编译的过程中,会把泛型信息"擦除"。
我们代码中写的--->编译器编译后的
E --> Object
? extends Animal --> Animal
? super Animal --> Object
*/
public class GenericDemo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
for(Iterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
System.out.println(s);
}
}
}
下面看一个题目:
package com.cskaoyan.jdk5;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
/**
* @author shihao
* @create 2020-05-15 15:35
* <p>
* 如何向List<String>中添加一个int类型的整数
*/
public class GenericDemo02 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
Class<? extends ArrayList> c1 = list.getClass();
//查看ArrayList中的add方法,boolean add(E e) ,其参数类型是: E
//那泛型E被擦除后会变成什么? Object
Method addMethod = c1.getMethod("add", Object.class);
//invoke()中第一个参数表示要在哪个对象上调用,第二个参数表示add方法中的形参
addMethod.invoke(list, 1);
System.out.println(list);
//如何获取这个整数
//1.用Object获取,然后强转
Object o = list.get(2);
int k = (int) o;
System.out.println(k);
//2.反射
}
}
反射的前提是先要获取字节码文件对象,由于泛型擦除,字节码文件对象里不会有泛型信息。
既然没有,String就起不到限制了,所以就可以利用反射机制向List中插入一个整数。
··············································································································································································································
ArrayList中的
T[ ] toArray(T[ ] a)
按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
package com.cskaoyan.jdk5;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/*
<T> T[] toArray(T[] a)
如果数组a能够容纳集合中的元素, 那么这个方法会把元素填充到数组a中。
如果数组a不能够容纳集合中的元素, 那么就会新创建一个数组, 并把集合的元素填充到新数组中。
*/
public class GenericDemo3 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
// String[] strs = new String[3];
// String[] strs = new String[4];
/*String[] strs = new String[2];
System.out.println(Arrays.toString(strs));
String[] arr = list.toArray(strs);
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(strs));
System.out.println(arr == strs);*/
//一般我们这样使用这个方法
String[] arr = new String[list.size()];
list.toArray(arr);
System.out.println(Arrays.toString(arr));
}
}
String[] arr = list.toArray(strs)
如果strs空间够用,arr和strs指向同一地址,即arr = strs为true
不够用,不会传入到strs中,arr == strs 为false
·················································································································································································································
增强for循环概述
简化数组和Collection集合的遍历
格式:
for(元素数据类型 变量 : 数组或者Collection集合) {
使用变量即可,该变量就是元素
}
好处:简化遍历
注意事项:增强for的目标要判断是否为null
本质:foreach对数组进行特殊处理, 会用迭代器遍历其它类型的对象。
问题:什么对象可以使用foreach循环?
数组和实现了Iterable接口的对象。
public class ForeachDemo1 {
public static void main(String[] args) {
String[] strs = {"刘亦菲", "王语嫣", "花木兰"};
for (int i = 0; i < strs.length; i++) {
System.out.println(strs[i]);
}
for(String s : strs) {
System.out.println(s);
}
System.out.println("----------------------");
List<String> list = new ArrayList<>();
list.add("赵灵儿");
list.add("白秀珠");
list.add("铁手");
for(Iterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
System.out.println(s);
}
for(String s : list) {
// System.out.println(s);
//ConcurrentModificationException 并发修改异常
if ("赵灵儿".equals(s)) list.remove(s);
}
}
}
foreach底层实现还是和以上代码一样
public interface Iterable 实现这个接口的对象,允许使用foreach循环。
·················································································································································································································
可变参数概述
定义方法的时候不知道该定义多少个参数,我们就可以使用可变长参数。
格式
修饰符 返回值类型 方法名(数据类型… 变量名){}
注意:
这里的变量其实是一个数组
可变长参数只能位于最后
底层就是一个数组,完全可以把可变长参数看成一个数组。
但是我们代码的可读性更高了。
public class VariableDemo1 {
/*public int add(int a, int b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}*/
// 如何求任意个整数之和呢?
/*public static int add(int[] arr) {
int sum = 0;
for(int i : arr) sum += i;
return sum;
}*/
public static int add(int... arr) {
int sum = 0;
for(int i : arr) sum += i;
return sum;
}
public static void main(String[] args) {
// int[] arr = {1, 2, 3, 4, 5};
// int sum = add(arr);
int a = 1;
int b = 2;
int c = 3;
int sum = add(1, 2, 3);
sum = add(a, b, c);
System.out.println(sum);
}
}
Arrays工具类中的一个方法
public static List asList(T… a)
集合 --> 数组
T[ ] toArray(T[ ] a)
数组 --> 集合
static List asList(T… a) 视图技术 (Arrays类中)
public class VariableDemo3 {
public static void main(String[] args) {
String[] arr = {"hello", "world", "java"};
List<String> list = Arrays.asList(arr);
// System.out.println(list);
//不支持增加操作
// list.add("collection"); // UnsupportedOperationException
// System.out.println(list);
//不支持删除操作
// list.remove(1); //UnsupportedOperationException
// System.out.println(list);
//支持更改操作
list.set(2, "JAVA");
System.out.println(list);
//修改后原数组中的数据也会改变
System.out.println(Arrays.toString(arr));
}
}
jdk5中的新特性其实就是把一些机械化的东西交给编译器去做,而程序员不需要关心这些事情
·················································································································································································································
数据结构
数据结构是相互之间存在一种或多种特定关系的数据元素的集合。
公式:数据结构=数据+结构
数据:用类型表示
结构:在任何问题中,数据元素都不是孤立存在的,它们之间都存在着某种关系,这种数据元素相互之间的关系称为结构。
元素之间,通常有以下四种基本结构:
集合:结构中的数据元素之间除了同属于一个集合的关系之外,别无其他关系。这里的集合和数学中集合的概念是一致的。
哈希表就属于集合
线性结构:结构中的数据元素之间存在一个对一个的关系。
树形结构:结构中的数据元素之间存在一个对多个的关系。
图或网状结构:结构中的数据元素之间,存在多个对多个的关系。
前面分类中定义的关系,描述的是数据元素间的逻辑关系,因此又称为逻辑结构。
但是仅仅知道数据元素间的逻辑关系是不够的,因为我们得实现自己的数据结构。
因此,我们得关注数据结构在计算机底层是如何表示的?
数据结构在计算机中的表示,称为数据的物理结构,又称为存储结构或者映像。
数据的表示很简单。
结构的表示可以分为两种:顺序存储结构 (顺序映像) 和 链式存储结构 (非顺序映像)。
顺序映像:借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系。(数组)
非顺序映像:借助指示元素存储地址的”指针”,来表示数据元素的逻辑关系。(链表)
·················································································································································································································
线性表:n个数据元素的有序序列。
首先,线性表中元素的个数是有限的。
其次,线性表中元素是有序的。
那这个”序”指的是什么呢?
如果以ai表示数据元素,则线性表可以记为 {a1, … , ai-1, ai, ai+1, … , an}
表中, ai-1在 ai 之前,同时ai+1 在 ai 之后,我们称ai-1是ai的直接前驱,ai+1是ai的直接后继。
除表头和表尾元素外,其它元素都有唯一前驱和唯一后继,其唯一前驱或唯一后继确定了该元素在线性表中的位置。
因此,线性表中,每个数据元素都有一个确定的位序,这个确定的位序我们称之为索引。
表头元素有唯一后继,无前驱,表尾元素有唯一前驱,无后继。
实现:
顺序映像 (ArrayList)
非顺序映像 (LinkedList)
·················································································································································································································
我们自己实现一个ArrayList