集合是Java中提供的一种容器,可以用来存储多个数据,可以对其中的数据进行操作
一、集合继承关系
由上图可以看到,Collection接口为集合中的顶层接口,Collection 接口下面分为两大部分,左边为 List 接口,右边为 Set 接口,在List 接口下面有两个分支,分别为 ArrayList 类和 LinkedList 类,在 Set 接口下面也有两个分支,分别为 HashSet 类和 LinkedHashSet 类
collection 接口常用的子接口有:List 接口、Set 接口
List 接口常用的子类有:ArrayList 类、LinkedList 类
Set 接口常用的子类有:HashSet 类、LinkedHashSet 类
二、Collection接口
Collection 接口是所有集合的顶层接口,其所有功能子类都可以使用,Collection 表示一组对象,这些对象也称为 collection 的元素,其中List 接口允许存放重复的元素,并且元素都是有序的,而Set 接口不允许存放重复元素,并且元素是无序的
1、Collection 集合创建格式
方法一:Collection<元素类型> 变量名 = new ArrayList<元素类型>(); ==>只能存储<>中指定的元素类型,比较常用的方式
方法二:Collection 变量名 = new ArrayList(); ==>集合的元素默认为Object类型,可以存储任何类型的元素
2、Collection 接口的基本方法
boolean add(Object obj):添加一个元素
void clear():移除collection中所有元素(集合还在)
boolean contains(Object obj):判断对象是否存在集合中
boolean remove(Object obj):移除集合中指定的元素(删除第一个遇到的元素)
int size():返回集合中元素的个数
Object[] toArray():把集合中的元素转成数组中的元素(集合转成数组)
逻辑实例:
Collection<String> C = new ArrayList<>(); //创建集合
C.add("abc"); //添加元素
C.add("bcd");
C.add("cef");
System.out.println(C.contains("d")); //判断元素是否在集合中
System.out.println(C.size()); //返回集合的元素个数
C.remove("abc"); //移除集合中的元素
Object[] O = C.toArray(); //将集合转换成数组
for(int i = 0;i < O.length;i++) //遍历打印数组
{
System.out.println(O[i]);
}
三、Iterator 迭代器
在集合中,由于采取的存储数据方式不同,获取数据方式也会不同,为了能够使用同一种方式来获取数据,Java 中采用 Iterator 迭代器来获取数据,即遍历获取,称之为迭代
Collection 集合获取数据的通用方法:先判断集合中有没有元素,如果有,就把元素取出来并继续判断,如果还有就继续取出,一直把集合中的元素全部取出。即迭代。集合中把这种取元素的方式描述在 Iterator 接口中,
1、Iterator 迭代器的实现
在ArrayList 中重写了方法 iterator(),返回了 Iterator 接口的实现类的对象
使用ArrayList集合的对象 array,Iterator it =array.iterator(),运行结果就是 Iterator 接口的实现类的对象
it 是接口的实现类对象,调用方法 hasNext 和 next 集合元素迭代
2、Iterator 迭代器中常用的方法
boolean hasNext():判断集合中是否还有元素可以迭代
next():获取出可以迭代的下一个元素
void remove():从迭代器指向的集合中移除迭代器返回的最后一个元素
逻辑实例:
Collection<String> C = new ArrayList<>(); //创建集合
C.add("abc"); //添加元素
C.add("bcd");
C.add("cef");
//迭代器,对集合ArrayList中的元素进行取出
//调用集合的方法iterator()获取出Iterator接口的实现类的对象
Iterator<String> it = C.iterator();
//迭代是反复内容,使用循环实现,循环的条件,集合中没元素, hasNext()返回了false
while(it.hasNext())
{
String s = it.next();
System.out.println(s);
}
四、集合迭代中的转型
当使用第二种方法:Collection 变量名 = new ArrayList(); 创建集合的时候,集合中就能存储任意类型的对象,存储时提升了 Object,这样一来,取出时要使用元素的特有内容,必须向下转型。所以说一般使用第一种方法创建集合,这种不常用。
逻辑实例:
Collection C = new ArrayList(); //创建集合
C.add("abc"); //添加元素
C.add("bcd");
C.add("cde");
Iterator it = C.iterator(); //调用集合的方法iterator()获取出,Iterator接口的实现类的对象
while (it.hasNext())
{
//由于元素被存放进集合后全部被提升为Object类型
//当需要使用子类对象特有方法时,需要向下转型
String str = (String) it.next();
System.out.println(str.length());
}
注:如果集合中存放的是多个对象,这时进行向下转型会发生类型转换异常,这时,就要采用泛型来解决这个问题
五、泛型
在进行迭代的转型的时候,如果集合中存放的是多个对象,进行向下转型就会发生 ClassCastExpception 的异常,这时,必须明确集合中元素的类型。这种方式称之为:泛型(在JDK1.5之后出现的)
1、泛型的定义
泛型表示:使用 < 元素类型 > 指明集合中存储数据的类型。如:Collection<元素类型> 变量名 = new ArrayList<元素类型>();
Java 中的伪泛型:泛型只在编译的时候存在,编译之后就擦除,在编译之前就可以限定其集合的类型
2、泛型类
格式:修饰符 class 类名<代表泛型的变量> { }
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。
3、泛型接口
格式:修饰符 interface 接口名 <代表泛型的变量>{ }
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中
//定义一个泛型接口
public interface Generator<T> {
public T next();
}
当实现泛型接口的类,未传入泛型实参时:
/**
* 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
* 即:class FruitGenerator<T> implements Generator<T>{
* 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
*/
class FruitGenerator<T> implements Generator<T>{
@Override
public T next() {
return null;
}
}
当实现泛型接口的类,传入泛型实参时:
/**
* 传入泛型实参时:
* 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>
* 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。
* 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
* 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。
*/
public class FruitGenerator implements Generator<String> {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
参考:https://blog.csdn.net/s10461/article/details/53941091
4、泛型通配符
泛型通配符 ‘ ?’是一个类型“实参”,不是一个类型“形参”。它可以被看作成所有类型(如Integer、String、Number)的父类,即 泛型类名称与同一泛型类名称并不是子父类的关系。具体来说就是,当我们想实现一个方法,方法的参数是 泛型类名称的时候,如果我们还想实现同一泛型类名称的时候,只能重新编写一个参数为 同一泛型类名称的方法,但这不符合java的多态思想,多态思想要求我们用一个逻辑引用类型就可以同时表示它们,这时候泛型通配符‘?’就排上了用场,把<>里面变成‘?’就把上面的问题解决了。
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
HashSet<Integer> set = new HashSet<Integer>();
array.add("123");
array.add("456");
set.add(789);
set.add(890);
iterator(array);
iterator(set);
}
/*
* 定义方法,可以同时迭代2个集合
* 参数: 怎么实现 , 不能写ArrayList,也不能写HashSet
* 参数: 或者共同实现的接口
* 泛型的通配,匹配所有的数据类型 ?
*/
public static void iterator(Collection<?> coll){
Iterator<?> it = coll.iterator();
while(it.hasNext()){
//it.next()获取的对象,什么类型
System.out.println(it.next());
}
}
5、泛型限定
public static void main(String[] args) {
//创建3个集合对象
ArrayList<Person> P = new ArrayList<Person>();
ArrayList<Teacher> T = new ArrayList<Teacher>();
ArrayList<Student> S = new ArrayList<Student>();
//每个集合存储自己的元素
P.add(new Person("张三", "休息001"));
P.add(new Person("李四", "休息002"));
T.add(new Teacher("王老师", "上课001"));
T.add(new Teacher("易老师", "上课002"));
S.add(new Student("小名", "学习001"));
S.add(new Student("小强", "学习002"));
iterator(P);
iterator(T);
iterator(S);
}
/*
* 定义方法,可以同时遍历3集合,遍历三个集合的同时,可以调用工作方法 work
* ? 通配符,迭代器it.next()方法取出来的是Object类型,怎么调用work方法
* 强制转换: it.next()=Object o ==> Employee
* 方法参数: 控制,可以传递Employee对象,也可以传递Employee的子类的对象
* 泛型的限定 本案例,父类固定Employee,但是子类可以无限?
* ? extends Employee 限制的是父类, 上限限定, 可以传递Employee,传递他的子类对象
* ? super Employee 限制的是子类, 下限限定, 可以传递Employee,传递他的父类对象
*/
public static void iterator(ArrayList<? extends Employee> array){
Iterator<? extends Employee> it = array.iterator();
while(it.hasNext()){
//获取出的next() 数据类型,是什么Employee
Employee e = it.next();
e.work();
}
}