【集合、迭代、泛型】
Collection集合
集合是java中提供的一种容器,可以用来存储多个数据。
集合和数组都是容器,两者之间的区别:
- 数组的长度是固定的,集合的长度是可变的。
- 数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象,而且对象的类型可以不一致,在开发中一般当对象多的时候,使用集合进行存储。
集合框架API
学习集合的目标:
- 1、会使用集合存储数据;
- 2、会遍历集合,把数据取出来;
- 3、掌握每种集合的特性。
集合框架的学习方式:
- 1.学习顶层:学习顶层接口(或者抽象类)中共性的方法,所有的子类都可以使用。
- 2.使用底层:顶层不是接口就是抽象类,无法对象使用,需要创建底层的子类对象使用。
Collection接口:
- 1.定义的是所有单列集合中共性的方法
- 2.所有的单列集合都可以使用共性的方法
- 3.没有带索引的方法
List接口:
- 1.有序的集合(存储和取出元素顺序相同)
- 2.允许存储重复的元素
- 3.有索引,可以使用普通的for循环遍历
Set接口:
- 2.不允许存储重复元素
- 3.没有索引,不能使用普通的for循环遍历,没有带索引的方法
注意:
- TreeSet、HashSet集合是无序的集合:存储和取出元素的顺序有可能不一致。
- LinkedSet是有序的集合。
各种集合类的特点
集合类 | 重要程度 | 特点 |
---|---|---|
ArrayList | 重点 | 底层是数组实现的,查询快,增删慢 |
LinkedList | 一般 | 底层是链表实现的,查询慢,增删快 |
Vector | 了解 | 底层是数组实现的,查询快,增删慢 |
HashSet | 重点 | 底层是哈希表+(红黑树)实现的,无索引,不可以存储重复元素,存取无序 |
LinkedHashSet | 一般 | 底层是哈希表+链表实现的,无索引,不可以存储重复元素,可以保证存取顺序 |
TreeSet | 了解 | 底层是二叉树实现,一般用于排序 |
集合常用方法
Java.util.Collection接口:
Collection接口是所有单列集合的最顶层的接口,里面定义了所有单列集合共性的方法。
任意的单列集合都可以使用Collection接口中的方法。
共性的方法:
方法名称 | 含义 |
---|---|
public boolean add(E e); | 把给定的对象添加到当前集合中 |
public void clear(); | 清空集合中的所有的元素 |
public boolean remove(E e); | 把指定的对象在当前集合中删除 |
public boolean contains(E e); | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty(); | 判断当前集合是否为空 |
public int size(); | 返回集合中元素的个数 |
public Object[] toArray(); | 把集合中的元素,存储到数组当中 |
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
public class ListandSet {
public static void main(String[] args) {
//创建集合对象,使用了多态
Collection<String> coll = new ArrayList<>();
System.out.println(coll);//[],打印出来的不是地址值,重写了toString方法
//add(E e)方法,返回的是booolean类型的值,一般为true,不用接收
boolean b1 = coll.add("James");
System.out.println("b1: " + b1);
System.out.println(coll);//[James]
coll.add("Durant");
coll.add("Curry");
coll.add("Davis");
coll.add("Irving");
System.out.println(coll);//[James, Durant, Curry, Davis, Irving]
//remove(E e)方法,返回值是boolean类型。
// 集合中存在该元素,则删除元素,返回true,否则,删除失败,返回false
boolean b2 = coll.remove("Irving");
System.out.println("b2:" + b2);//true
boolean b3 = coll.remove("Harden");
System.out.println("b3:" + b3);//false
System.out.println(coll);//[James, Durant, Curry, Davis]
//contains(E e)方法,判断当前集合中是否包含给定的对象
//包含,返回true;不包含,返回false
boolean b4 = coll.contains("Giannis");
System.out.println("b4:" + b4);//false
boolean b5 = coll.contains("James");
System.out.println("b5:" + b5);//true
//isEmpty()方法,返回boolean值,判断该集合是否为空
boolean b6 = coll.isEmpty();
System.out.println("b6:" + b6);//false
//size()方法,返回int值,该集合的长度
int l = coll.size();
System.out.println("l:" + l);
//toArray()方法,返回的是Object[]
Object[] array = coll.toArray();
System.out.println(array[2]);//Curry
//clear()方法,清空集合中所有的元素,但是不删除集合,集合还存在
coll.clear();
System.out.println(coll);//[]
}
}
Iterator迭代器
Iterator接口
迭代是Collection集合元素的通用获取方式。在取元素之前要先判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就要再取出来。一直到把集合中的所有元素全部取出。这种取出方式专业术语成为迭代。
Iterator接口的常用方法如下:
public E next()
:返回迭代的下一个元素public boolean hasNext()
:如果仍有元素可以迭代,则返回true。
Iterator迭代器是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象。
获取实现类对象的方法比较特殊。
Collection接口中有一个方法,叫做iterator(),这个方法返回的就是迭代器的实现类对象。
- Iterator iterator() 返回在此 collection 的元素上进行迭代的迭代器
迭代器使用步骤:
- 先使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
- 使用Iterator接口中的方法hasNext()判断还有没有下一个元素
- 使用Iterator接口中的方法next()取出集合中的下一个元素
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Iterator02 {
public static void main(String[] args) {
//多态 接口 = 实现类对象
Collection<Integer> coll = new ArrayList<>();
coll.add(8);
coll.add(8);
coll.add(4);
coll.add(8);
System.out.println(coll);
//多态 接口 = 实现类对象
Iterator<Integer> iterator = coll.iterator();
//使用实现类对象的hasNext()方法判断是否还有元素
while(iterator.hasNext()){
//使用实现类对象的next()方法取出元素
System.out.println(iterator.next());
}
}
}
迭代器的实现原理
在coll调用iterator()方法的时候,返回了Iterator类型的实现类对象iterator,并且把指针(索引)指向集合的-1索引。
iterator对象调用hasNext()方法,判断集合中还有下一个元素。
iterator对象调用next()方法,做了两件事情:
- 取出下一个元素
- 把指针向后移动一位
增强for循环
增加for循环(也称for each循环)是JDK 1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。
格式:
for(元素的数据类型 变量:Collection集合or数组){
//写操作代码
}
它用于遍历Collection和数组。通常只进行遍历元素,不要在遍历的过程中对集合元素进行增删操作。
Collection extends Iterable: 所有的单列集合都可以使用增强for;
public interface Iterable:实现这个借口允许对象成为"foreach"语句的目标。
import java.util.ArrayList;
import java.util.Collection;
public class ReforceFor {
public static void main(String[] args) {
demo01();
demo02();
}
//使用增强for循环遍历数组
public static void demo01() {
int[] arr = {1, 2, 3};
for (int i : arr) {
System.out.println(i);
}
}
//使用增强for循环遍历集合
public static void demo02() {
Collection<Integer> collection = new ArrayList<>();
collection.add(8);
collection.add(8);
collection.add(4);
collection.add(8);
for (int a : collection) {
System.out.println(a);
}
}
}
泛型
泛型的概念
泛型是一种未知的数据类型,当我们不知道使用什么数据类型的时候,可以使用泛型。
泛型也可以看出是一个变量,用来接收数据类型。
- E e:Element 元素
- T t: Type 类型
ArrayList集合在定义的时候,不知道集合中都会存储什么类型的数据,所以类型使用泛型。
创建集合对象的时候,就会确定泛型的数据类型。
使用泛型的好处
创建集合对象,不使用泛型。
好处:
- 集合不使用泛型,默认的类型就是Object类型,可以存储任意类型的数据。
弊端:
- 不安全,会引起异常
public class Element01 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("abc");
list.add(2);
//使用迭代器遍历list集合
//获取迭代器
Iterator iterator = list.iterator();
while(iterator.hasNext()){
//取出的元素也是Object类型的
Object obj = iterator.next();
System.out.println(obj);
//想要使用String类型特有的方法length获取字符串的长度,不能使用 多态 Object obj = "abc";
//需要向下转型
//会抛出ClassCastException类型转换异常,不能把Integer类型转换为String类型
String s = (String) obj;
System.out.println(s.length());
}
}
}
创建集合对象,使用泛型。
好处:
- 1.避免了类型转换的麻烦,存储的是什么类型,取出的就是什么数据类型
- 2.把运行期异常(代码运行之后才会抛出的异常),提前到了编译期(写代码的时候就会抛出异常)
弊端:
- 泛型是什么类型,就只能存储什么类型的数据
定义和使用含有泛型的类
定义一个含有泛型的类。
泛型是一个未知的数据类型,当我们不确定使用什么数据类型的时候,可以使用泛型。
泛型可以接受任意的数据类型,可以是Integer,String,Student…
创建对象的时候确定泛型的数据类型。
public class GenericClass<E> {
private E name;
public GenericClass() {
}
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
public GenericClass(E name) {
this.name = name;
}
}
public class GenericClassTest {
public static void main(String[] args) {
//创建GenericClass对象,泛型使用Integer类型
GenericClass<Integer> gc1 = new GenericClass<>();
gc1.setName(2);
int i = gc1.getName();//自动拆箱
System.out.println(i);
//创建GenericClass对象,泛型使用String类型
GenericClass<String> gc2 = new GenericClass<>();
gc2.setName("小明");
String name = gc2.getName();
System.out.println(name);
}
定义和使用含有泛型的方法
泛型定义在方法的修饰符和返回值类型之间。
格式:
修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)){
方法体;
}
含有泛型的方法,在调用方法的时候确定泛型的数据类型。
传递什么类型的参数,泛型就是什么类型。
public class GenericMethod {
//定义一个含有泛型的方法
public <M> void method01(M m){
System.out.println(m);
}
//定义一个含有泛型的静态方法
public static <S> void method02(S s){
System.out.println(s);
}
}
public class GenericMethodTest {
public static void main(String[] args) {
GenericMethod gm = new GenericMethod();
//调用含有泛型的方法,传递什么类型,泛型就是什么类型
gm.method01(1);
gm.method01(true);
gm.method01("22");
gm.method01(6.5);
GenericMethod.method02("静态方法");
GenericMethod.method02(9.7);
GenericMethod.method02(88);
}
}
定义和使用含有泛型的接口
含有泛型的接口,第一种定义方法:定义接口的实现类,实现接口,指定接口的泛型。
如:
public interface Iterator {
E next();
}
Scanner类实现了Iterator,并指定接口的泛型为String,所以重写的next()方法,泛型默认为String类:
public final class Scanner implements Iterator{
next(String str);
}
public interface GenericInterface<I> {
public abstract void method(I i);
}
public class GenericInterfaceImpl implements GenericInterface<String> {
@Override
public void method(String s) {
System.out.println(s);
}
}
public class GenericInterfaceTest {
public static void main(String[] args) {
GenericInterfaceImpl gi1 = new GenericInterfaceImpl();
gi1.method("只能是字符串");//只能是字符串
}
}
含有泛型的接口,第二种定义方法:接口使用什么泛型,实现类就是用什么泛型,类跟着接口走,就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型。
如:
public interface List{
boolean add(E e);
E get(int index);
}
public class ArrayList implements List{
boolean add(E e);
E get(int index);
}
public interface GenericInterface<I> {
public abstract void method(I i);
}
public class GenericInterfaceImpl2<I> implements GenericInterface<I> {
@Override
public void method(I i) {
System.out.println(i);
}
}
public class GenericInterfaceTest {
public static void main(String[] args) {
GenericInterfaceImpl2<Integer> gi2 = new GenericInterfaceImpl2<>();
gi2.method(88);//88
}
}
泛型通配符
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
此时只能接受数据,而不能往该集合中存储数据。
使用方法:
- 不能创建对象使用;
- 只能作为方法的参数使用
import java.util.ArrayList;
import java.util.Iterator;
public class Generic01 {
public static void main(String[] args) {
ArrayList<String> list01 = new ArrayList<>();
list01.add("a");
list01.add("b");
ArrayList<Integer> list02 = new ArrayList<>();
list02.add(6);
list02.add(7);
printArrayList(list01);
printArrayList(list02);
}
public static void printArrayList(ArrayList<?> list){
for(Object a : list){
System.out.println(a);
}
}
}
通配符的高级使用——受限泛型
- 泛型的上限限定:? extends E 代表使用的泛型必须是E类型的子类/本身
泛型的下限限定:? super E 代表使用的泛型必须是E类型的父类/本身