泛型:
- 只存在于编译时期
- 意义:自动进行类型的检查;自动进行类型的转换
- 范式在编译的时候,并不会进行指定类型的替换,而是拿着指定的类型进行检查
- 编译的时候,会进行类型擦除,编译的时候,编译都会把泛型擦除为Object,不是替换为Object。
- 例:写一个通用的顺序表
class MyArrayList<T>{
public T[] elem;
public int usedSize;
public MyArrayList(){
// 不能直接new范型类,需要将Object向下类型转换为T类型的数组
this.elem = (T[])new Object[10];
}
public void add(T data){
this.elem[this.usedSize] = data;
this.usedSize++;
}
public T get(int pos){
return this.elem[pos];
}
public static void main(String[] args){
// 简单类型不能做范型类型的参数
MyArrayList<String> myArrayList = new MyArrayList<>(); // 放字符型
myArrayList1.add("abcd");
myArrayList1.add("edfc");
String str = myArrayList1.get(1);
System.out.println(str);
MyArrayList<Integer> myArrayList2 = new MyArrayList<>(); // 放整形
myArrayList2.add(1);
myArrayList2.add(2);
int val = myArrayList2.get(1);
System.out.println(val);
MyArrayList<Double> myArrayList3 = new MyArrayList<>(); // 放浮点型
}
}
- 1、
class MyArrayList<T>
,当中:表示占位符,表示当前这个类是一个泛型类 - 2、简单类型不能做泛型类型的参数
- 3、不能直接new泛型类型
- 4、泛型类型的参数不参与类型的组成(泛型只是)
包装类
- 装包/装箱操作:简单类型变为包装类
public static void main(String[] args) {
int a = 10;
Integer integer1 = new Integer(a);//显示的装包
System.out.println(integer1);
Integer integer2 = Integer.valueOf(a);//显示装包
System.out.println(integer2);
Integer integer3 = a;//自动装包,这种装包方法其实也是调用了Integer.valueOf
}
- 拆箱/拆包操作:包装类变为简单类型
public static void main(){
Integer i = 10;
int a= i; // 自动拆箱 -> i.intValue()
int a2 = i.intValue(); // 显示拆箱int
double d = i.doubleValue(); // 拆成double类型
}
- 一道面试题:代码如下会出现两种情况,如何解释?
public static void main(String[] args) {
Integer a = 200;
Integer b = 200;
System.out.println(a == b); // false
Integer c = 100;
Integer d = 100;
System.out.println(c == d); // true
}
- 解析:将简单类型装包
Integer a = 200
,底层会执行Integer.valueOf()
方法,此方法如下:此处i就是200.
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
代码中 IntegerCache.low = -128
IntegerCache.high = 127
所以 i 的取值只能在这之间
cache[] 是一个缓存数组 cache[]中存值的范围{-127 ~ 128}
- 为什么要这样?如果i不在范围内,则就会new一个新的对象,但当在这个范围内时,取的数都是在这个数组内,所以比较的是数组内的值。源码这样设计是为了更高效,节省空间。
List的使用
- 实现List的类ArrayList:
public static void main1(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
/*
ArrayList底层是一个数组,每次放元素的时候
放在了数组的最后。public boolean add(E e) {
public void add(int index, E element) {
这个方法是放在index位置
问题:ArrayList底层是数组 那么他是多大??
1、new ArrayList<>() 调用的是 不带有参数的构造方法
那么大小默认为0。
2、当调用饿了默认的构造方法之后,当你添加第一个元素的时候
会进行扩容,第一次扩容的时候,大小为10;
3、当后续进行扩容的时候 ,是进行了1.5倍的方式进行扩容
*/
list.add(1);
System.out.println(list); // 输出:[1]
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(10);
arrayList.add(20);
list.addAll(arrayList); // 在list的基础上插入一个arrayList
System.out.println(list);// 输出为:[1,10,20]
// T remove(int index) 删除index位置元素
// boolean remove(Object o) 删除遇到的第一个o
// 当给的是一个整形1,即int = 1,就会匹配到remove(int index)
list.remove(0); // 删除0下标的值
System.out.println(list); // 输出为:[10, 20]
// 如果我想删除的是[1,10,20]中的1,而不是1下标
// 所以要传入一个Object,传入Integer类
Integer integer = 1;
list.remove(integer);
System.out.println(list);
list.set(0,99); // 把0下标改为99
System.out.println(list);
}
- subList需要注意的事项:
public static void main2(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
System.out.println(list);
List<Integer> list1 = new ArrayList<>();
list1 = list.subList(1,3); //会提取1下标,2下标和3下标,输出为[2,3] ,返回值是一个List
list1.set(0,88); // 将list1的0下标改为88
System.out.println(list1);// 则list1就变为了:[88,3]
System.out.println(list); // 但list确也变成了[1,88,3,4,5]
此处的subList是浅拷贝,并没有new新的对象
在源码中时使用list1引用指向了list中的[2,3]部分
- iterator迭代器:用来打印集合中的元素的
public static void main2(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
System.out.println(list);//1 2 3 4 5
//迭代器 用来打印集合中的元素的
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) { //hasNext,是否有下一个
//iteerator 是在数组的前一个位置,所以打印的是iterator.next
System.out.println(iterator.next());
}
// 慎用iterator.remove
iterator.remove(); // 会删除最后一个数
System.out.println(list);
}
- 1、Java中,的集合类型包括ArrayList、LinkList、HashMap等,下列关于集合描述错误的是?(C)
A.ArrayList和LinkedList均实现了List接口
B.ArrayList的访问速度比LinkedList快
C.随机添加和删除元素时,ArrayList的表现更佳
D.HashMap实现Map接口,它允许任何类型的键和值对象
解析:ArrayList是基于数组实现的,所以查询快,增删慢;LinkedList是基于链表实现的,所以查找慢,增删快。
*2、下列在Java语言中关于数据类型和包装类的说法,正确的是( B )
A.基本(简单)数据类型是包装类的简写形式,可以用包装类替代基本(简单)数据类型
B.long和double都占了64位(64bit)的存储空间。
C.默认的整数数据类型是int,默认的浮点数据类型是float。
D.和包装类一样,基本(简单)数据类型声明的变量中也具有静态方法,用来完成进制转化等。
- 解析:
1、整数类型byte(1个字节)short(2个字节)int(4个字节)long(8个字节)
2、字符类型char(2个字节)
3、浮点类型float(4个字节)double(8个字节)
练习-创建一副扑克牌
public class pokerCard {
public int rank; // 牌面值
public String suit; // 花色
@Override
public String toString() {
return "[" +
rank +","+
suit +
']';
}
}
public class CardDemo {
// 扑克牌中花色是固定不变的,使用final修饰,
public static final String[] SUITS = {"♦", "♥", "♣", "♠"};
// 依据花色创建一副牌
private static List<pokerCard> CreatPoker(){
List<pokerCard> deck = new ArrayList<>(52); // 创建大小为52的数组来存放扑克牌
for (int i = 0; i < 4; i++) { //遍历花色列表
for(int j = 1; j <= 13; j++) {
String suit = SUITS[i];
int rank = j;
pokerCard card = new pokerCard();
card.rank = rank;
card.suit = suit;
deck.add(card); //将牌加入到扑克牌数组
}
}
return deck;
}
// 洗牌
public static void swap(List<pokerCard> deck, int i, int j){
pokerCard t = deck.get(i);
deck.set(i, deck.get(j));
deck.set(j, t);
}
public static void Shuffle(List<pokerCard> deck){
Random random = new Random(12);
for (int i = deck.size() - 1; i >0 ; i--) {
int r = random.nextInt(i);
swap(deck, i, r);
}
}
public static void main(String[] args) {
// 创建poker
List<pokerCard> deck = CreatPoker();
System.out.println(deck);
// 洗牌
Shuffle(deck);
System.out.println(deck);
// 有三个人,每人拿五张牌
List<List<pokerCard>> hands = new ArrayList<>();
hands.add(new ArrayList<>());
hands.add(new ArrayList<>());
hands.add(new ArrayList<>());
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
hands.get(j).add(deck.remove(0));
}
}
System.out.println("剩余的牌:");
System.out.println(deck);
System.out.println("A 手中的牌:");
System.out.println(hands.get(0));
System.out.println("B 手中的牌:");
System.out.println(hands.get(1));
System.out.println("C 手中的牌:");
System.out.println(hands.get(2));
}
}
练习-杨辉三角
- 给定一个非负整数numRow,实现杨辉三角前numRows行。杨辉三角中,每个数是它左上方和右上方的数之和。
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> ret = new ArrayList<>();
if(numRows <= 0) return ret;
//第一行的list
List<Integer> list = new ArrayList<>();
list.add(1);
//把第一行的list放到ret当中
ret.add(list);
for(int i = 1;i < numRows;i++) {
List<Integer> curRow = new ArrayList<>();
curRow.add(1);
for(int j = 1 ;j < i;j++) {
//确定的是当前行的每个元素 == 上一行的当前列+上一行的前一列就是我当前需要添加的数字
List<Integer> preRow = ret.get(i-1);
int num = preRow.get(j-1)+preRow.get(j);
curRow.add(num);
}
//手动在当前行的最后一个位置 添加一个1
curRow.add(1);
ret.add(curRow);
}
return ret;
}