1.泛型
1.1概念
public class LinkedList extends
Abstract Sequential implements List
public interface Deque extends Queue { }
public interface Queue extends Collection { }
public interface Collection extends Iterable { }
上面的代码中出现的<?>是泛型,常用来和集合对象一同使用,所以在开始学习集合之前,必须先了解一下什么是泛型。而且泛型的概念非常重要,它是程序的增强器,它是目前主流的开发方式。
泛型是JDK1.5提供的新特性,其实就是一个【语法糖】,本质上就是编译器为了提供更好的可读性而提供的一种小技巧,虚拟机层面是不存在所谓的泛型概念的。
1.2作用
通过泛型的语法定义<>,约束集合元素的类型,编译器可以在编译器提供一定的安全检查,这样可以避免运行时候才暴露bug,代码的通用性也会更强,泛型可以提升代码的可读性,但是它只是一个“语法糖”(编译后这样的部分会被删除,不会出现在源码中,所以不会影响JVM后期运行时的性能)
1.3泛型实例
实例1:我们创建一个ArrayList,看到界面上会有一个黄线警告,是为什么呢?
原因:ArrayList定义时候使用了泛型,在声明泛型时候需要制定具体的额类型
我们把这个“<>”方式称之为泛型,那么泛型有什么样的作用呢?就是在啊编译检查阶段检查传入的参数是否正确。
有了泛型,我们可以看到要求存放的是String类型,而测试存放的是int类型的100,所以eclipse会报错:
类型list 的add方法要求添加的类型为String类型,int类型不匹配,不能正确存入。
1.4泛型声明
泛型可以在接口、类、方法上使用
public interface Collection{}
public class TestStudy{}
public void print (E e){}
在方法返回值前声明一个,表示后面出现的E是泛型,而不是普通的Java变量
1.5常用名词
E - Element 在集合中使用,因为集合中存放的是元素
T - Type Java类
K - Key 键
V - value 值
N - number 数值类型
?- 表示不确定的Java类型
1.6泛型测试
package day14;
import java.util.ArrayList;
/**
* 本类用于测试泛型的优点
*/
public class Test1_Generic {
public static void main(String[] args) {
//1.泛型是怎么来的:想要模仿数组的数据类型检查
String[] a = new String[5];
a[2] = "马乐乐";
a[3] = "龙王爷";
//a[0] = 100;数组的好处,在编译时期检查数据的类型,如果不是要求的数据类型就会在编译器时期报错
//2.泛型通常会结合着集合一起使用
ArrayList<Object> list = new ArrayList<>();
list.add("白龙马");
list.add(18);
list.add('a');
list.add(2.2);
System.out.println(list);
//3.引入泛型--主要目的是想约束集合中元素的类型<?>
//4.泛型的好处:可以把报错的时机提前,在编译器就报错,而不是在运行后抛出异常,
// 在向集合中添加元素时,会先检查元素的数据类型,不是要求的数据类型就会编译失败
ArrayList<String> list1 = new ArrayList<>();
list1.add("小龙女");
// list1.add(18);存入的是Integer类型,会报错
// list1.add(2.3);存入的是Double类型,会报错
// list1.add('a');存入的是Character类型,会报错
//5.<type>---type的值应该如何写?
//需要查看存放的数据类型是什么,根据类型进行定义
//注意这里Type的类型必须是引用类型不能是基本类型
// new ArrayList<int>();会报错:Type argument cannot be of primitive type
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(222);
list2.add(333);
list2.add(666);
System.out.println(list2);
}
}
1.7练习:泛型测试2
package day14;
public class Test2_Generic2 {
public static void main(String[] args) {
Integer[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
print(a);
String[] b = {"林黛玉", "贾宝玉", "施耐庵", "林冲"};
print(b);
Double[] c = {2.1, 2.2, 2.3, 2.4};
print(c);
}
public static <E> void print(E[] e) {
//泛型的语法要求:如果在方法上面使用泛型,需要两处同时出现,
//一个是在传入的参数类型,一个是返回值前的类型,表示这是一个泛型
for (E d : e) {
System.out.println(d);
}
}
// public static void print(Double[] c) {
// for (Double d:c)
// {
// System.out.println(d);
// }
// }
//
// public static void print(String[] b) {
// for (String s:b)
// {
// System.out.println(b);
// }
// }
//
// public static void print(Integer[] a) {
// for (Integer i:a) {
// System.out.println(i);
// }
}
2.Collection接口
Java的语言中Util包中提供了一些集合类,这些集合类由称之为容器,提到容器不难想到数组,集合类和数组最主要的不同之处在于,数组的长度是固定的,集合的长度是可变的,数组的访问方式比较单一,插入、删除等操作比较繁琐,而集合的访问方式比较灵活,常用的集合类有list集合,Set集合,Map集合,其中Set集合和List集合继承了Collection接口,各个接口还提供了不同的实现类。
2.1概述
集合的英文名称是Collection,用来存放对象的数据结构,而且长度可变,可以存放不同类型的独享,并且还提供了一组操作成批对象的方法。Collection接口层次结构中的根接口,接口不能直接使用,但是该接口提供了添加元素、删除元素、管理元素的父类接口公共方法。
由于List接口与set接口都继承了Collection接口,因此这些方法对于List集合和Set集合都是通用的。
2.2 集合的集成结构
Collection接口
—List接口:数据有下标,所以数据是有序的,可以重复存数值
—ArrayList子类
—LinkedList子类
—Set接口:数据是没有下标的,所以数据是无序的,不可以存重复的值
—HashSet子类
Map接口:键值对的方式存数据(注意:Map是一种接口,是和Collection同层级的,并不是collection子接口)
—HashMap
2.3常用方法速查表
2.4练习:Collection接口
package day14;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
public class Test3_Collection {
public static void main(String[] args) {
// Collection collection = new Collection<>();
//Collection是接口不能直接实例化
ArrayList<Integer> c = new ArrayList<>();
c.add(100);
c.add(200);
c.add(300);
c.add(400);
c.add(500);
System.out.println(c);
// c.clear();清除c中所有元素
// System.out.println(c);
System.out.println(c.contains(100));
System.out.println(c.isEmpty());
System.out.println(c.remove(1));
System.out.println(c);
System.out.println(c.size());
System.out.println(c.equals(200));
Object [] array = c.toArray();
System.out.println(Arrays.toString(array));
Collection<Integer> c2 = new ArrayList<Integer>();
c2.add(2);
c2.add(3);
c2.add(5);
System.out.println(c2);//[2, 3, 5]
c.addAll(c2);
System.out.println(c);//[100, 300, 400, 500, 2, 3, 5]
System.out.println(c2);//[2, 3, 5]
System.out.println(c.containsAll(c2));//true
System.out.println(c.contains(c2));//false,判断C中是否有C2这个元素
System.out.println(c.removeAll(c2));//true
System.out.println(c.containsAll(c2));//false
//使用迭代器来遍历元素
Iterator<Integer> it = c.iterator();
while (it.hasNext()){
System.out.println(it.next());//输出c中所有元素
}
}
}
3.List接口
3.1概述
有序的collection(也称为序列),此接口的用户可以对列表中的每个元素插入位置进行精确的控制,用户可以根据元素的整数索引(在列表中的位置)来访问元素,并搜索列表中的元素。
3.2特点
- 元素都有下标
- 数据是有序的
- 允许存放重复的元素
3.3常用方法速查表
3.4练习:List接口测试
package day14;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Test4_List {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("大公主红红");
list.add("二公主橙橙");
list.add("三公主黄黄");
list.add("四公主绿绿");
list.add("五公主青青");
list.add("六公主蓝蓝");
list.add("七公主紫紫");
System.out.println(list);//[大公主红红, 二公主橙橙, 三公主黄黄, 四公主绿绿, 五公主青青, 六公主蓝蓝, 七公主紫紫]
// list.clear();//清空集合
// System.out.println(list);//[]
System.out.println("----------------------------------");
System.out.println(list.contains("董勇"));//false
System.out.println(list.equals("大公主红红"));//false
System.out.println(list.hashCode());//-49722495
System.out.println(list.get(3));//四公主绿绿,按照索引来获取元素
System.out.println(list.remove("大公主红红"));
System.out.println(list);//[二公主橙橙, 三公主黄黄, 四公主绿绿, 五公主青青, 六公主蓝蓝, 七公主紫紫]
System.out.println(list.remove(0));
System.out.println(list);//[三公主黄黄, 四公主绿绿, 五公主青青, 六公主蓝蓝, 七公主紫紫]
//对比上面两个方法可以看出remove在使用String的时候,即可以用index方式按照下标移除
//也可以用“String”里面的字符串内容做匹配来进行移除
System.out.println(list.size());//5
System.out.println(Arrays.toString(list.toArray()));//转化为字符串
list.add("土地老儿");
System.out.println(list);//尾部追加//[三公主黄黄, 四公主绿绿, 五公主青青, 六公主蓝蓝, 七公主紫紫, 土地老儿]
list.add(1, "龙太子");
System.out.println(list);//指定位置添加//[三公主黄黄, 龙太子, 四公主绿绿, 五公主青青, 六公主蓝蓝, 七公主紫紫, 土地老儿]
list.add(4, "龙太子");
System.out.println(list);//指定位置添加已存在元素//[三公主黄黄,龙太子,四公主绿绿,五公主青青,龙太子,六公主蓝蓝,七公主紫紫,土地老儿]
list.set(2, "白龙马");
System.out.println(list);//[三公主黄黄, 龙太子, 白龙马, 五公主青青, 龙太子, 六公主蓝蓝, 七公主紫紫, 土地老儿]
System.out.println(list.indexOf("龙太子"));//1
System.out.println(list.lastIndexOf("龙太子"));//4
List<String> list2 = list.subList(2, 6);
System.out.println(list2);//[白龙马, 五公主青青, 龙太子, 六公主蓝蓝]
System.out.println(list.contains(list2));//false
System.out.println(list.containsAll(list2));//true
System.out.println(list.removeAll(list2));//true
System.out.println(list);//[三公主黄黄, 七公主紫紫, 土地老儿]
}
}
3.5练习:List接口测试2
package day14;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
public class Test5_List2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("大叔");
list.add("二叔");
list.add("三叔");
list.add("四叔");
list.add("五叔");
list.add("六叔");
list.add("七叔");
//方式1:测试循环迭代,因为元素下标有序,根据下标循环
for (int i = 0; i < list.size(); i++) {
String string = list.get(i);
System.out.print(string);
}
System.out.println("-----------");
//方式2:高效for循环
for (String s:list) {
System.out.print(s);
}
System.out.println("-----------");
//方式3:list迭代器
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
System.out.print(iterator.next());
}
System.out.println("----------");
//方式4:ArrayList迭代器
ListIterator<String> iterator1 = list.listIterator();
while (iterator1.hasNext()){
System.out.print(iterator1.next());
}
/**
* 思考:方式3和方式4有什么不同
* 3使用的是父接口中的iterator,4使用的是子接口中的iterator
* 子接口拥有父接口中的所有方法,初次之外,子接口也可以拥有自己特有的方法
*/
}
}
4.ArrayLi
4.1 概述
- 存在于java.util包中
- 内部是用于数组结构存放数据,封装数组的操作,每个对象都有下标
- 内部数组默认的初试容量是10,如果不够会以1.5倍的容量增长
- 查询快,增删效率比较低
4.2创建对象
ArrayLi()创建一个初试容量为10的空序列
源码摘抄:int newCapacity = oldCapacity + (oldCapacity >> 1);
解释:数组的新容量=旧容量 /2的一次方----相当于原来的1.5倍的扩容(找时间把这里的源码再锊一边)
4.3练习:ArrayList数组
package day14;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
public class Test6_ArrayList {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(100);
list.add(200);
list.add(300);
list.add(100);
list.add(400);
list.add(200);
list.add(300);
list.add(0, 700);//[700, 100, 200, 300, 100, 400, 200, 300]
System.out.println(list);
// list.clear();
// System.out.println(list);//[]
System.out.println(list.contains(200));//true
System.out.println(list.get(0));//700
System.out.println(list.indexOf(100));//1
System.out.println(list.lastIndexOf(100));//4
System.out.println(list.isEmpty());//false
System.out.println(list.set(0, 666));//true
System.out.println(list);//[666, 100, 200, 300, 100, 400, 200, 300]
System.out.println(list.remove(1));
System.out.println(list);//[666, 200, 300, 100, 400, 200, 300]
// System.out.println(list.remove(200));会报错,根据下标来移除
System.out.println(list.remove(Integer.valueOf(200)));//true
System.out.println(list);//[666, 300, 100, 400, 200, 300],按照从前到后的下标移除
//循环方式1
for (int i = 0; i < list.size(); i++) {
Integer integer = list.get(i);
System.out.println(integer);
}
//循环方式2
for (Integer i : list) {
System.out.println(i);
}
//循环方式3
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
//循环方式4
ListIterator<Integer> iterator1 = list.listIterator();
while (iterator1.hasNext()) {
System.out.println(iterator1.next());
}
}
}
7 LinkedList
7.1 概述
链表,两端效率高,底层就是链表实现的
总结:
ArrayList底层是数组结构,查询快,增删慢,适合查询比较多的业务场景
LinkedList底层是链表结构,查询慢,增删快,适合增删比较多的业务场景
注意:LinkedList查询慢是指业务数据量 比较大的时候,查询中间比较慢,首尾操作还是比较快的
7.2创建对象
LinkedList()构造一个空列表
7.3常用方法
Void add First(E e)将指定元素插入此列表的开头
Void add Last(E e)将指定元素插入此列表的结尾
E. getFirst() 返回此列表的第一个元素
E.getLast ( ) 返回此列表的最后一个元素
E. removeFirst() 移除此列表的第一个元素
E. removelast() 移除此列表的最后一个元素
E element ( ) 获取但不移除此列表的头(第一个元素)
boolean offer( E e)将指定元素添加到此列表的末尾(最后一个元素)
boolean offerFirst ( E e)在此列表的开头插入指定的元素
boolean offerLast(E e)在此列表的末尾插入指定的元素
E peek() 获取但不移除此列表的头(第一个元素)
E peekFirst() 获取但不移除次列表的尾(最后一个元素)
E pollFirst()获取并移除此列表的第一个元素;如果此列表为空,则返回null
E pollLast()获取并移除此列表的最后一个元素;如果此列表为空,则返回null
7.4: LinkedList测试
package day14;
import java.util.LinkedList;
public class Test7LinkedList<S> {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("林黛玉");
list.add("贾宝玉");
list.add("薛宝钗");
list.add("史湘云");
list.add("呆霸王");
list.add("贾祖母");
System.out.println(list);//[林黛玉, 贾宝玉, 薛宝钗, 史湘云, 呆霸王, 贾祖母]
list.addFirst("晴雯");
System.out.println(list);//[林黛玉, 贾宝玉, 薛宝钗, 史湘云, 呆霸王, 贾祖母]
list.addLast("雪雁");
System.out.println(list);//[晴雯, 林黛玉, 贾宝玉, 薛宝钗, 史湘云, 呆霸王, 贾祖母, 雪雁]
System.out.println(list.removeFirst());//晴雯
System.out.println(list);//[林黛玉, 贾宝玉, 薛宝钗, 史湘云, 呆霸王, 贾祖母, 雪雁]
System.out.println(list.removeLast());
System.out.println(list);//[林黛玉, 贾宝玉, 薛宝钗, 史湘云, 呆霸王, 贾祖母]
LinkedList list1 = new LinkedList();
list1.add("飘");
list1.add("红与黑");
list1.add("救赎");
list1.add("茶花女");
System.out.println(list1);//[飘, 红与黑, 救赎, 茶花女]
System.out.println(list1.element());//飘
System.out.println(list1);//[飘, 红与黑, 救赎, 茶花女]
System.out.println(list1.peek());//飘
System.out.println(list1);//[飘, 红与黑, 救赎, 茶花女]
System.out.println(list1.peekFirst());//飘
System.out.println(list1);//[飘, 红与黑, 救赎, 茶花女]
System.out.println(list1.peekLast());//茶花女
System.out.println(list1);//[飘, 红与黑, 救赎, 茶花女]
System.out.println(list1.offer("基督山伯爵"));//true
System.out.println(list1);//[飘, 红与黑, 救赎, 茶花女, 基督山伯爵]
System.out.println(list1.offerFirst("海底两万里"));//true
System.out.println(list1);//[海底两万里, 飘, 红与黑, 救赎, 茶花女, 基督山伯爵]
System.out.println(list1.offerLast("李尔王"));
System.out.println(list1);//[海底两万里, 飘, 红与黑, 救赎, 茶花女, 基督山伯爵]
System.out.println(list1.poll());//海底两万里
System.out.println(list1);//[飘, 红与黑, 救赎, 茶花女, 基督山伯爵, 李尔王]
System.out.println(list1.pollFirst());//飘
System.out.println(list1.pollLast());//李尔王
System.out.println(list1);//[红与黑, 救赎, 茶花女, 基督山伯爵]
}
}
8 扩展
ArrayList扩容
ArrayList相当于在没指定initalcapacity时就是会延时分配对象数组,当第一次插入元素时候才会分配10(默认)个对象空间。假如有20个数据需要添加,那么会分别在第一次的时候,将ArrayList的容量变为10,之后扩容会按照1.5倍增长,也就是当添加第11个元素的时候,ArrayList会继续扩容变成101.5=15,当添加第16个元素的时候,继续扩容为151.5 = 22个,以此类推。
ArrayList没有对外暴露其容量个数,查看源码我们知道,实际其值存放在elementDate对象数组中,那么我们只需要拿到这个数组的长度,观察其值变化了几次就知道扩容了几次,可以用反射技术获取。