一、泛型的概念
1、泛型长什么样?
<类型>
2、为什么要有泛型?
Java5决定对API做修整。
原来设计集合(不仅仅是集合)等类型时,并不确定集合中的元素是什么类型的。
集合是一种容器,在Java中是用来装对象的容器。
它只能设计成可以装任意类型的对象。
导致:(1)不安全(2)麻烦
生活中:瓶子,可以装各种液体。
假设所有的瓶子都没有标签。那么超市中所有瓶子都装了各种液体,但是没有说明是什么。
客户就不敢买。 或者说 买的时候,需要打开闻一闻、尝一尝才能确定是什么。
中药铺。柜子/抽屉。生产柜子的厂家一开始不知道这个抽屉中用来装什么,什么都可以往里装。
中药铺,如果所有抽屉都没有标签。很危险。
泛型就是为了解决类型安全的问题。同时还可以避免类型转换,使得使用更方便。
换句话说,泛型的好处:(1)安全(2)使用更简便
Java中编写方法,现在知道方法的功能是什么,例如:求两个整数的最大值。
public int max(int a, int b){
return a > b ? a : b;
}
未知的数据:两个整数,通过形参表示。
因为形参不需要在编写方法时确定值,由调用者在调用时确定它的值
受形参这种形式的启发,我们设计了泛型。
泛型分为类型形参和类型实参。代表类型。 为了区分,把方法()中的称为数据形参和数据实参。代表数据。
ArrayList, 这个E:类型形参
ArrayList:这个String:类型实参
public class TestGeneric {
/* public int max(){
int a;
int b;
//如果不是形参,就需要在这里手动初始化
return a > b ? a : b;
}*/
//如果使用成员变量,那么就需要在创建对象时,为a,b初始化。如果此时这个a,b只是在这个方法中使用,a,b 的交给对象,就太麻烦了
/* int a;
int b;
public int max(){
return a > b ? a : b;
}*/
//形参的好处:在设计这个方法时,不需要确定它的值,在调用时,再由使用者确定它的值
public int max(int a, int b){
return a > b ? a : b;
}
@Test
public void test01(){
ArrayList list = new ArrayList();//容器,集合的对象
list.add("hello");
list.add("java");
list.add(1);//Integer对象 1会自动装箱为Integer的对象
list.add(new Date());
//要从这个集合中取出一个元素
Object value = list.get(3);//3是索引,下标
System.out.println(value);
//如果按照我们程序一开始的意图,我本来是想着往这个集合中装的都是String。
// 发现取出来的是Date对象,不安全
//就算我知道元素是字符串,取出来也要进行类型的转换,很麻烦
String obj = (String) list.get(0);
}
@Test
public void test03() {
//在使用ArrayList确定了元素的/泛型的类型是String
ArrayList<String> list = new ArrayList<String>();//容器,集合的对象
list.add("hello");
list.add("java");
// list.add(1);//报错,可以提前避免不符合的类型的对象,装到这个容器中,提高了安全性
// list.add(new Date());//报错
String s = list.get(0);//不需要类型转换,使用更简便了。
}
public void test02(){
Student stu = new Student();
int result = stu.compareTo(new Date());
}
}
/*
如果没有泛型,Comparable是表示任意类型的对象的自然比较规则,设计为Object类型
*/
class Student implements Comparable{
private String name;
private int score;
@Override
public int compareTo(Object o) {
//必须向下转型
Student stu = (Student) o;//有风险
return 0;
}
}
二、泛型的语法
1、泛型的使用常见分为两大类
(1)泛型类、泛型接口
(2)泛型方法
2、泛型类、泛型接口
(1)声明一个泛型类或泛型接口的语法格式:
【修饰符】 class 类名<泛型类型形参列表> 【extends 父类】【implements 接口们】{
//…
}
【修饰符】 interface 接口名<泛型类型形参列表> 【extends 父接口们】{
//…
}
例如:源码中
public interface Comparable
public interface Comparator
public abstract class Enum<E extends Enum> implements Comparable, Serializable
public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable
自定义一个:
例如:声明一个学生类型,但是这个学生类型中的成绩的类型未知。
因为使用这个学生管理系统的老师对学生的成绩的类型不能统一。
语文老师:成绩应该标识为:优秀、良好、及格、不及格.. String
数学老师:成绩应该标识为:89.5,34.0.... double
英语老师:成绩应该标识为:A,B,C,D... char
(2)如何确定泛型形参的具体类型?
A:在创建这个类的对象时,可以指定泛型形参的具体类型
B:在继承泛型类或实现泛型接口时,可以指定泛型形参的具体类型
注意:泛型实参类型必须是引用数据类型,不能是基本数据类型。
泛型形参是代表对象的类型。
(3)泛型类上的泛型形参的使用要求?
A:强烈建议大家泛型形参使用单个的大写字母,不要使用单词
因为单词容易和现有的API中类型或自定义的其他类型无法区别。
B:泛型类/接口上的泛型形参这个类型绝对不能用于类或接口中的静态成员上
C:泛型类/接口上的泛型形参的类型可以设置上限
<泛型形参 extends 父类/父接口>
这个上限还可以是多个。
例如:<T extends Number & Comparable & Cloneable>
要求上限的类只有一个,而且必须在第一个,接口可以多个。
D:泛型类/接口上的泛型形参的类型可以多个
例如:ArrayList
Map<K,V>
(4)泛型的擦除
如果泛型形参没有上限,泛型擦除(没有指定时)按照Object处理。
如果泛型形参设定上限,泛型擦除(没有指定时)按照第一个上限处理。
public class TestGenericClass {
@Test
public void test06(){
//泛型的擦除
ArrayList list= new ArrayList();
XueSheng x = new XueSheng();
XueYuan x2 = new XueYuan();
// x2.setScore();//形参类型识别为Number
}
@Test
public void test01(){
//现在是语文老师在用
XueSheng<String> x = new XueSheng<String>("张三","优秀");
}
@Test
public void test02(){
//现在是数学老师在用
// XueSheng<double> x = new XueSheng<double>("张三",89.5);
//不能使用基本数据类型,改用包装类
XueSheng<Double> x = new XueSheng<Double>("张三",89.5);
Double score = x.getScore();
}
@Test
public void test03(){
EnglishXueSheng x = new EnglishXueSheng();
// x.setScore(89.0);
x.setScore('A');
}
@Test
public void test04(){
//使用有泛型上限的泛型类XueYuan<T extends Number>
// XueYuan<String> x = new XueYuan<>();//不能指定String,因为String不是Number或Number的子类
XueYuan<Integer> x1 = new XueYuan<Integer>();//不能指定String,因为String不是Number或Number的子类
XueYuan<Double> x2 = new XueYuan<Double>();//不能指定String,因为String不是Number或Number的子类
}
}
/*
在XueSheng这个类中,凡是表示学生的成绩的类型,统统都用T表示。
T:泛型形参
这个T的类型什么时候明确?
*/
class XueSheng<T>{
private String name;
//这个学生类型中的成绩的类型未知。
private T score;//这个成绩在整个类中都要使用,所以在类名后面用一个泛型形参来代表它的未知的类型
public XueSheng() {
}
public XueSheng(String name, T score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public T getScore() {
return score;
}
public void setScore(T score) {
this.score = score;
}
@Override
public String toString() {
return "XueSheng{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
//英语老师,你的给我专门生成一个学员的类型
//英语的老师的学生成绩是确定的,是char(Character)
class EnglishXueSheng extends XueSheng<Character>{
}
//实现泛型接口:java.lang.Comparable<T>
//它的抽象方法:public int compareTo(T o);
class Employee implements Comparable<Employee>{
private int id;
private String name;
@Override
public int compareTo(Employee o) {
return this.id - o.id;
}
}
//以下声明泛型类的形式是不建议的
/*
class MyClass1<String>{
private String info;
}
class MyClass2<Type>{
private Type other;
}*/
//以下使用泛型类上的泛型形参是错误的
//我们说T在(1)new对象时(2)继承时确定,而调用method方法时,可能没有对象,也没有继承,所以T是未知的
class MyClass<T>{
/* public static void method(T t){
//....
}*/
}
//以下声明泛型形参,指定了上限
//假设声明这个学生的类型时,成绩的类型也不确定,但是成绩限定为数值类型,不能是非数值类型
//数值类型:int,byte,short,long,double,float,不能是String等
//所有数值类型都是java.lang.Number的子类
//这个T必须是Number或Number的子类
class XueYuan<T extends Number>{
private T score;
public void setScore(T score) {
this.score = score;
}
}
//这个T必须是Number或Number的子类,并且这个类型实现了Comparable和Cloneable接口
class XueYuan2 <T extends Number & Comparable<T> & Cloneable>{
}
//以下泛型类,声明了多个泛型形参
class Demo<T,U,K,V>{
}
3、泛型方法
(1)什么是泛型方法,语法格式
【修饰符】 <泛型类型形参列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{
}
说明:A: <泛型类型形参列表> ,泛型形参类型可以多个,例如:,<T,U>
B:泛型形参类型也可以指定上限,例如:<T extends Comparable>
C:泛型方法中的泛型形参类型,只在本方法有效
(2)为什么会需要泛型方法呢?
A:泛型类或泛型接口上面的泛型类型是不能用于静态成员上的,
那么当静态方法中想要使用泛型时,就需要单独声明泛型类型。
B:当某个类或接口不是泛型类或泛型接口,现在想要增加一个方法,这个方法是非静态方法,
它的形参类型或返回值类型未知,也可以单独为这个方法声明泛型形参
例如:我要声明一个数组工具类MyArrays,它包含一个排序方法
public static void sort(Object[] arr)
public class TestGenericMethod {
@Test
public void test01(){
Student[] arr = new Student[2];
arr[0] = new Student();
arr[1] = new Student();
MyArrays.sort1(arr);//运行会报错,因为Student没有实现Comparable接口
}
@Test
public void test02(){
Student[] arr = new Student[2];
arr[0] = new Student();
arr[1] = new Student();
// MyArrays.sort2(arr);//编译会报错,因为Student没有实现Comparable接口,把隐患提前告知
}
}
class Student{
}
class MyArrays{
//按照元素的自然顺序比较大小
//自然顺序就是Comparable<T>接口的int compareTo(T t)的比较规则,就是自然顺序的比较规则
//不知道数组的元素的类型,使用Object处理,需要类型转换,不安全,也麻烦
public static void sort1(Object[] arr){
for (int i=1; i<arr.length; i++){
for (int j=0; j<arr.length-i; j++){
//比较两个对象的大小,为了调用compareTo方法,必须向下转型为Comparable<T>
Comparable c = (Comparable) arr[j];//有风险,只有运行时,这个问题才能显示出来
if(c.compareTo(arr[j+1]) > 0){
Object temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] =temp;
}
}
}
}
public static <T extends Comparable<T>> void sort2(T[] arr){
for (int i=1; i<arr.length; i++){
for (int j=0; i<arr.length-i; j++){
//arr[j]是T类型,T一定是实现了Comparable接口的类型
if(arr[j].compareTo(arr[j+1]) > 0){
T temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] =temp;
}
}
}
}
}
三、泛型的通配符
1、为什么会有泛型的通配符?
当我们使用某个泛型类/接口声明变量的时候,按理说,我们需要为它的泛型形参指定具体的类型。
例如:
class XueSheng,使用它是应该:XueSheng t;
class XueSheng,使用它是应该:XueSheng t;
但是,如果此时使用它的时候,这个具体的类型也不能确定,那怎么办?
A:泛型擦除
B:泛型通配符
例如:用这个泛型类/接口声明形参
2、形式
(1)<?>:?代表任意类型
只读
(2)<? extends 上限>:?代表上限或上限的子类
只读
(3)<? super 下限>:?代表下限或下限的父类
修改只能接收下限或下限的子类类型的对象。
例如: <? super Number>
实参: XueSheng或XueSheng
T类型的score只能设置为Number的子类Integer的对象
public class TestWild {
@Test
public void test01(){
XueSheng<String> xueSheng1 = method1(new XueSheng<String>());
XueSheng<Double> xueSheng2 =method1(new XueSheng<Double>());
XueSheng<Integer> xueSheng3 =method1(new XueSheng<Integer>());
String s1 = xueSheng1.getScore();
Double s2 = xueSheng2.getScore();
Integer s3 = xueSheng3.getScore();
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
@Test
public void test02(){
XueSheng<String> xueSheng1 = method2(new XueSheng<String>());
XueSheng<Double> xueSheng2 =method2(new XueSheng<Double>());
XueSheng<Integer> xueSheng3 =method2(new XueSheng<Integer>());
String s1 = xueSheng1.getScore();
Double s2 = xueSheng2.getScore();
Integer s3 = xueSheng3.getScore();
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
//?不是代表Object,而是代表未知的任意引用数据类型
public XueSheng method2(XueSheng<?> x){//擦除,按照Object,可以,就是麻烦点
// x.setScore("优秀");//?虽然是代表任意类型,但是这个值变为只读,不能修改
//因为?是表示任意类型,不确定类型,那么你赋值为什么值都是错误的
return x;
}
//XueSheng后面没有写<>,表示擦除
public XueSheng method1(XueSheng x){//擦除,按照Object,可以,就是麻烦点
x.setScore("优秀");//形参T,按照Object处理,有风险
x.setScore(12.8);
x.setScore(1);
return x;
}
@Test
public void test03(){
// method3(new XueSheng<String>());//错误,String不是Number或它子类
method3(new XueSheng<Double>());
method3(new XueSheng<Integer>());
}
public void method3(XueSheng<? extends Number> x){
//因为?代表Number或Number的任一种子类,也是不确定的,可能是Integer,也可能是Double
//如果是Double,设置为1就是错误的
// x.setScore(1);//不能修改
}
@Test
public void test04(){
// method4(new XueSheng<String>());//错误,String不是Number或它的父类
// method4(new XueSheng<Double>());//错误,String不是Number或它的父类
// method4(new XueSheng<Integer>());//错误,String不是Number或它的父类
method4(new XueSheng<Object>());
}
public void method4(XueSheng<? super Number> x){
x.setScore(1);//可以,因为?代表Number或Number的父类,可以接收Integer对象
// x.setScore("优秀");//错误,因为?代表Number或Number的父类,String类型不是Number家族
x.setScore(1.0);//可以,因为?代表Number或Number的父类,可以接收Double对象
}
}
class XueSheng<T>{
private T score;
public void setScore(T score) {
this.score = score;
}
public T getScore() {
return score;
}
@Override
public String toString() {
return "XueSheng{" +
"score=" + score +
'}';
}
}
一、集合(非常重要)
开发中必用,面试必考。
1、集合是什么?用来干什么的?
集合是容器,是用来装对象的容器。
变量也是可以说是容器,装一个数据值。
数组也是容器,既可以用来装基本数据类型的值,也可以用来装对象。
集合是容器,是用来装对象的容器。不能装基本数据类型的值。如果把基本数据类型的值装到集合中,会自动装箱为包装类的对象。
2、容器分为很多种。
它把容器分为两大类,抽取它们的共同特征,主要抽取的是行为特征,即行为标准,形成了两大接口:
(1)Collection:这个系列的集合是装一组对象的
比喻:单身party
(2)Map:这个系列的集合是装一对一对的映射关系的,映射关系(key,value)
比喻:情侣party,家庭party
二、Collection系列
1、Collection系列的集合和之前学习的数组有什么不同?
(1)数组的长度是不可变、集合的大小是可变的
(2)实际开发中,一般使用数组存储基本数据类型的值,使用集合装对象比较多
2、java.util.Collection接口:这里的E是ElementType的首字母,表示元素的类型。即这个E是代表集合元素的类型。
先针对集合(容器)都有什么操作。
往容器中放对象,取对象,查询对象,修改对象,简称增删该查。
(1)添加
boolean add(E e ):添加一个元素对象
boolean addAll(Collection<? extends E> c) :添加多个元素对象,把c集合中的所有元素添加到当前集合中,
<? extends E>:上限,c集合中的元素的类型<=当前集合中元素类型E
this = this ∪ c;
(2)查询
A:查询集合中元素的个数
int size()
B:查询集合是否包含某个/某些元素
boolean contains(Object o) :判断当前集合是否包含o对象
boolean containsAll(Collection<?> c) :判断当前集合是否包含c集合中的所有元素
即判断c集合是否是当前集合的子集
C:判断当前集合是否是空集合
boolean isEmpty()
(3)删除
void clear() :清空集合
boolean remove(Object o) :删除一个对象
boolean removeAll(Collection<?> c) :删除多个对象
this = this - this ∩ c;
boolean retainAll(Collection<?> c) :保留部分元素,删除this和c不同的元素
this = this ∩ c;
(4)修改(没有)
(5)集合的遍历
遍历:挨个访问集合中的元素
A:把集合转为数组(了解)
Object[] toArray()
B:使用迭代器遍历(掌握)
Iterator iterator()
迭代:等价于遍历,挨个访问集合中的元素
迭代器:专门遍历集合等容器的对象。
所有迭代器的抽象接口类型:java.util.Iterator
它的对象不是程序员new的,而是通过集合对象.iterator()获取的
3、如何使用Iterator迭代器?
(1)boolean hasNext() :判断当前迭代器游标后面是否还有元素可以迭代/访问。
(2)E next():取出当前游标指向的元素
(3)void remove() :删除刚刚迭代的元素
当我们使用Iterator迭代器遍历集合的时候,不能调用集合自己的remove删除方法,而要用迭代器自己的remove方法。
*/
public class TestContainer {
@Test
public void test12() {
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("java");
c.add("atguigu");
//删除元素中长度>5的字符串
//使用集合的remove()或removeAll()无法实现
//只能一边遍历,一边根据条件删除
Iterator<String> iterator = c.iterator();
while(iterator.hasNext()){
String element = iterator.next();
if(element.length()>5){
// iterator.remove();
//以下写法错误,但是很容易犯
// c.remove(element);//ConcurrentModificationException:并发修改集合的异常
//因为c.remove(element)这个方法删除是独立与迭代器之外的,集合自己的删除方法,它删除不会通知迭代器
//迭代器在遍历集合之初有一个游标,它一开始是要记录集合的元素个数的,
//此时集合把元素删除了,迭代器不知道,游标就会指向不存在的位置。
}
}
System.out.println(c);//[hello, java]
}
@Test
public void test11() {
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("java");
c.add("atguigu");
//访问集合中的字符串元素的长度
//iterator是迭代器对象,用来遍历集合的
Iterator<String> iterator = c.iterator();
while(iterator.hasNext()){
String element = iterator.next();
System.out.println(element + "长度:" + element.length());
}
}
@Test
public void test10() {
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("java");
c.add("atguigu");
//转为数组遍历
//访问每一个字符串元素的长度
Object[] objects = c.toArray();
for (int i = 0; i < objects.length; i++) {
Object object = objects[i];
System.out.println(object + "长度为:" + ((String)object).length());
}
}
@Test
public void test09() {
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("java");
c.add("atguigu");
Collection<String> c2 = new ArrayList<>();
c2.add("hello");
c2.add("java");
// c2.add("ag");
System.out.println(c.contains("hello"));//true
System.out.println(c.containsAll(c2));//true
}
@Test
public void test08() {
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("java");
c.add("atguigu");
Collection<String> c2 = new ArrayList<>();
c2.add("hello");
c2.add("java");
c2.add("chailinyan");
c.retainAll(c2);
System.out.println(c);//[hello, java]
}
@Test
public void test07() {
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("java");
c.add("atguigu");
Collection<String> c2 = new ArrayList<>();
c2.add("hello");
c2.add("java");
c2.add("chailinyan");
c.removeAll(c2);
System.out.println(c);//[atguigu]
System.out.println(c2);//[hello, java, chailinyan]
}
@Test
public void test06() {
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("java");
c.add("atguigu");
//remove方法删除元素是调用元素的equals方法
c.remove("hello");
c.remove(new String("java"));
System.out.println(c);//[atguigu]
}
@Test
public void test05() {
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("java");
c.add("atguigu");
//集合的删除
c.clear();
System.out.println(c);
}
@Test
public void test04() {
Collection c1 = new ArrayList();
c1.add(1);
c1.add(2);
Collection c2 = new ArrayList();
c2.add(3);
c2.add(4);
c1.add(c2);//add()添加一个对象,此处把c2当成一个对象添加
System.out.println(c1);//[1, 2, [3, 4]]
}
@Test
public void test03() {
Collection<Number> c1 = new ArrayList<>();
c1.add(1);
c1.add(2);
//Integer<Number,是Number的子类
Collection<Integer> c2 = new ArrayList<>();
c2.add(3);
c2.add(4);
// c1.add(c2);//add()添加一个对象,此处把c2当成一个对象添加
c1.addAll(c2);
System.out.println(c1);//[1, 2, 3, 4]
}
@Test
public void test02(){
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("java");
c.add("atguigu");
System.out.println("元素的个数:" + c.size());
System.out.println("集合的元素有:"+c);//打印对象,自动调用对象的toString方法
//集合的元素有:[hello, java, atguigu] 说明ArrayList集合重写了toString方法
}
@Test
public void test01(){
/*
Collection<E>是接口,是不能直接new对象的,必须创建它的实现类的对象。
它的实现类有很多,最常用的有:ArrayList<E>
这里写多态引用的目的是为了让大家关注Collection接口的方法,
因为多态引用,这个变量c编译时类型是按照Collection处理,无法调用子类或实现类增加的方法。
*/
Collection<String> c = new ArrayList<>();
/*
Collection<String> c = new ArrayList<String>(); //标准的指定泛型的格式
Collection<String> c = new ArrayList<>();//右边<>没有省略,这种写法是JDK1.7之后才支持,和上面一样
Collection c = new ArrayList<String>();//编译时按左边类型编译,相当于擦除了Collection<E>这个泛型,E按照Object处理
//运行时按照右边处理,泛型是<String>类型。
Collection c = new ArrayList();//编译时按左边类型编译,相当于擦除了Collection<E>这个泛型,E按照Object处理
*/
}
}
java.lang.Comparable与java.util.Comparator:
自然比较接口 定制比较接口
java.lang.Iterable与java.util.Iterator
Iterable 表示可迭代的,实现它表示可以使用foreach遍历。
Iterator表示迭代器的公共接口
使用foreach遍历,本质上是使用 Iterator迭代器在迭代/遍历集合。
4、java.lang.Iterable接口
刚才迭代器:java.util.Iterator迭代器接口
Iterable接口的抽象方法:
Iterator iterator()
凡是实现Iterable接口的集合等类型,必须实现Iterator iterator(),提供迭代器对象,用于foreach遍历。
A:Java中的所有数组类型都实现了Iterable接口。表示所有数组都可以使用foreach遍历。
B:Java中的Collection接口继承了Iterable接口,意味着凡是实现Collection接口的,就会实现Iterable接口。
5、什么是foreach循环
(1)语法结构
for(元素的类型 元素名 : 数组/Collection集合名/其他实现了Iterable接口的容器对象名){
}
(2)使用foreach注意
因为它本质上也是使用Iterator进行迭代的,所以在使用foreach遍历集合的过程中,
也不要调用集合的add,remove等方法来修改集合的元素。
换句话说,使用foreach遍历集合只能查看,或通过集合对象修改对象的属性。但是修改集合元素的个数。
public class TestIterable {
@Test
public void test02(){
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("java");
c.add("atguigu");
//String:元素的类型
//str:自己命名,表示元素名称
//c:集合对象名
for (String str : c) {
System.out.println(str);
}
}
@Test
public void test01(){
int[] arr = {1,2,3,4,5};
//快捷模板:iter
//int:元素的类型
//element:自己命名,表示元素名称
//arr:表示数组名
for (int element : arr) {
System.out.println(element);
}
}
}
Collection 层次结构 中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。
一些 collection 允许有重复的元素,而另一些则不允许。
一些 collection 是有序的,而另一些则是无序的。
JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。
三、List系列的集合
1、List系列的集合的特点:
(1)允许元素重复
(2)有序的(也称为序列):可以根据元素的【index】来精确的访问某个元素
2、List接口比Collection接口更具体一点点,它增加了很多新的方法,这些方法都是和[index]有关
List是Collection的子接口,Collection接口有的它也有,它还有Collection没有,
下面主要演示Collection没有的方法:
(1)添加
void add(int index, E element)
boolean addAll(int index, Collection<? extends E> c)
(2)删除
E remove(int index)
(3)修改
E set(int index, E element) :修改/替换[index]位置的元素为element
(4)查询
E get(int index) :获取[index]位置的元素
int indexOf(Object o) :查询o在当前集合中下标,如果有多个,返回第一个的下标,如果没有,返回-1
int lastIndexOf(Object o) :查询o在当前集合中下标,如果有多个,返回最后一个的下标,如果没有,返回-1
List subList(int fromIndex, int toIndex) :截取[fromIndex,toIndex)部分的元素构成一个新的List集合
(5)遍历
原来Collection的遍历方式仍然支持
新增了:
ListIterator listIterator()
ListIterator listIterator(int index)
3、ListIterator迭代器,它是Iterator的子接口
Iterator迭代器有三个方法:
(1)boolean hasNext()
(2)E next()
(3)void remove()
ListIterator增加了方法:
列表迭代器,允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置。
支持在遍历的过程中,增删改查元素:add(E e),remove(),set(E e),E next()
支持在遍历过程中,获取下标信息:nextIndex(),previousIndex()
支持任一方向遍历:next(), previous();
public class TestList {
@Test
public void test12() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("atguigu");
ListIterator<String> listIterator = list.listIterator(list.size());
//list.listIterator(list.size()):游标在最后
System.out.println("从后往前遍历:");
while(listIterator.hasPrevious()){
System.out.println(listIterator.previous());
}
}
@Test
public void test11() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("atguigu");
ListIterator<String> listIterator = list.listIterator();
//list.listIterator();方法,游标在最前面,所以下面的循环不进去
System.out.println("从后往前遍历:");
while(listIterator.hasPrevious()){
System.out.println(listIterator.previous());
}
}
@Test
public void test10() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("atguigu");
ListIterator<String> listIterator = list.listIterator();
System.out.println("从前往后遍历:");
while(listIterator.hasNext()){
System.out.println(listIterator.next());
}
System.out.println("---------------------------");
System.out.println("从后往前遍历:");
while(listIterator.hasPrevious()){
System.out.println(listIterator.previous());
}
}
@Test
public void test09() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("atguigu");
int count = 0;
for (String s : list) {
System.out.println("第" + ++count +"个元素:"+ s);
}
System.out.println("---------------------------");
count = 0;
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
String next = iterator.next();
System.out.println("第" + ++count +"个元素:"+ next);
}
System.out.println("-------------------------");
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()){
String next = listIterator.next();
System.out.println("第" + listIterator.nextIndex() +"个元素:"+ next);
}
}
@Test
public void test08() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("atguigu");
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()){
String s = listIterator.next();
if(s.equals("world")){
listIterator.set("java");
}
}
System.out.println(list);//[hello, java, atguigu]
}
@Test
public void test07() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("atguigu");
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()){
String s = listIterator.next();
if(s.equals("world")){
listIterator.add("java");//迭代器的增加元素的方法
}
}
System.out.println(list);//[hello, world, java, atguigu]
}
@Test
public void test06() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("hello");
list.add("java");
list.add("atguigu");
//[0,2)
List<String> subList = list.subList(0, 2);
System.out.println(subList);//[hello, world]
}
@Test
public void test05() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("hello");
list.add("java");
list.add("atguigu");
System.out.println(list.get(1));
System.out.println(list.indexOf("hello"));
System.out.println(list.lastIndexOf("hello"));
}
@Test
public void test04() {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("hello");
list.add("java");
list.add("atguigu");
list.set(2,"chailinyan");
System.out.println(list);
}
@Test
public void test03() {
List<Integer> list = new ArrayList<>();
list.add(10);
list.add(2);
list.add(3);
list.add(1);
list.add(5);
/*
list.remove(int index)
list.remove(Object obj)
list.remove(1)和list.remove(int index)更匹配
*/
// list.remove(1);
list.remove(new Integer(1));//删除元素值是1的元素
System.out.println(list);//[1, 3, 4, 5]
}
@Test
public void test02(){
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("hello");
list.add("java");
list.add("atguigu");
System.out.println(list);
list.remove(2);
System.out.println(list);
}
@Test
public void test01(){
//这里左边使用List,也是为了关注List接口中的方法
//注意导包不要导错了,java.util.List(集合)和java.awt.List(做图形化界面的下拉列表用)
List<String> list = new ArrayList<>();
list.add("张三");
list.add(0,"李四");
System.out.println(list);//[李四,张三]
}
}