集合和泛型
文章目录
集合Collection
集合由来
开发应用程序时,如果想储存多个同类型的数据,可以使用数组来实现,但是使用数组存在如下一些明显缺陷:
-
数组长度固定不变,不能很好地适应元素数量动态变化的情况。
-
可通过数组名.length获取数组的长度,却无法直接获取数组中实际储存的元素个数。
-
数组采用在内存中分配连续空间的存储方式存储,根据元素信息查找时效率比较低,需要多次比较。
因此,Java提供了比数组更灵活、更实用的集合框架,可大大提高软件的开发效率,并且不同的集合适用于不同的应用场景。
Java集合框架提供了一套性能优良、使用方便的接口和类,它们都位于java.util包中。
集合继承体系
Collection接口
Collection接口是集合中的顶层接口,那么它中定义的所有功能子类都可以使用。查阅API中描述的Collection接口。Collection 层次结构中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。
创建集合的格式:
Collection 变量名 = new ArrayList();//父类引用指向子类对象
collection接口通用方法
/* 集合中只能存储引用数据类型,不可能存储基本类型,存储的基本类型其实也是自动装箱的
* Collection接口的通用方法:
* boolean add(Object e):将传过来的数据添加集合容器中
*
* void clear():清空集合中所有,便于重复利用这个集合
boolean isEmpty():判断集合是否为空
int size():获取集合中存储的元素的个数
boolean contains(Object o):判断集合中是否包含指定元素,如果包含返回true,如果不包含返回false
boolean remove(Object o):从集合中删除指定元素,如果删除成功,返回true,如果删除失败返回false
集合相对与数组的优点:我们在使用集合的时候,不用手动指定集合的长度,而数组必须指定长度
*/
import java.util.ArrayList;
import java.util.Collection;
public class Demo {
public static void main(String[] args) {
Collection c= new ArrayList();//父接口指向子类对象
c.add("abc");//Object o="abc",多态的使用方式
c.add("def");
System.out.println(c);//[abc, def] 中括号中的数据代表集合中的元素
}
}
import java.util.ArrayList;
import java.util.Collection;
public class Demo {
//boolean add(Object o)
//尝试用add方法存储基本类型
public static void main(String[] args) {
Collection c= new ArrayList();//父接口指向子类对象
c.add(2);//先把2自动装箱-->new Integer(2),然后再把这个Integer对象给了Object o
//Object o=new Integer(2);//多态
c.add(2.3);//装箱成new Double(2.3)
System.out.println(c);//[2,2.3]
}
}
//clear()方法
public static void main(String[] args) {
Collection c= new ArrayList();//父接口指向子类对象
c.add("abc");
c.add("def");
System.out.println(c);//[abc, def]
c.clear();
System.out.println(c);//[]
}
//isEmpty()判断集合是否为空
public static void main(String[] args) {
Collection c= new ArrayList();
c.add("abc");
boolean b=c.isEmpty();
System.out.println(b);//false
}
//size()获取集合中存储的元素的个数
public static void main(String[] args) {
Collection c= new ArrayList();
c.add("abc");
c.add("def");
c.add("ghk");
System.out.println(c);//[abc, def, ghk]
int i=c.size();//c中有3个元素
System.out.println(i);//3
}
//contains方法与remove方法()
public static void main(String[] args) {
Collection c= new ArrayList();
c.add("令狐冲");
c.add("岳灵珊");
boolean b1=c.contains("岳灵珊");
boolean b2=c.contains("岳不群");
System.out.println(b1+" "+b2);//true,false
boolean b3=c.remove("岳灵珊");
System.out.println(b3);//true
System.out.println(c);//[令狐冲]
boolean b4=c.remove("任盈盈");
System.out.println(b4);//false
System.out.println(c);//[令狐冲]
}
集合遍历
普通for循环遍历
普通for循环遍历集合的思想和遍历数组的思想相似,但是我们的Collection中并没有提供根据索引获取元素的方法,我们需要去使用子类ArrayList中的get(int index)方法来获取元素
public static void main(String[] args) {
Collection c = new ArrayList();//ArrayList()集合底层会对元素编号从0开始
c.add("李雷");//对应0索引
c.add("韩梅梅");//对应1索引
c.add("李梅");//对应2索引
for (int i = 0; i < c.size(); i++) {
ArrayList al = (ArrayList) c;//强制向下转型,为了使用子类特有方法
Object obj = al.get(i);//第一次循环:Object obj=al.get(0);//Object obj="李雷"
//第二次循环:Object obj=al.get(1);//Object obj="韩梅梅"
//....
System.out.println(obj);
}
}
迭代器遍历
interface Collection{
Iterator<E> iterator();
}
Interface Iterator<E>{
boolean hasNext();
E next();
}
/*1.获取迭代器
* c.iterator();
*2.利用迭代器的hasNext()方法,判断集合中是否有待取的元素
* 如果有,返回true
*3.利用迭代器的next()方法取出当前的元素
*4.直到hasNext()返回false,停止遍历*/
在Collection接口描述了一个抽象方法iterator方法,所有Collection子类都实现了这个方法,并且有自己的迭代形式。
java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。我们要取出这些集合中的元素,可通过一种通用的获取方式来完成。
Collection集合元素的通用获取方式:在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
集合中把这种取元素的方式描述在Iterator接口中。Iterator接口的常用方法如下:
-
hasNext()方法:用来判断集合中是否有下一个元素可以迭代(遍历)。如果返回true,说明可以迭代。
-
next()方法:返回当前遍历到的元素。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*Collection中的获取迭代器方法:
* Iterator iterator()
* 返回一个迭代器对象,我们利用这个迭代器对象对集合进行遍历
*接口Iterator中的方法
* boolean hasNext()
判断当前遍历的集合中是否还有元素,有返回true,没有返回false
Object next()
取出当前遍历到的元素
void remove()
删除当前遍历到的元素
*/
public class Demo {
public static void main(String[] args) {
//1.获取集合中的一个迭代器对象
Collection c = new ArrayList();
c.add("李雷");
c.add("韩梅梅");
c.add("李梅");
Iterator it = c.iterator();//c.iterator()一定返回Iterator的实现类对象
//多态思想
//Calendar c=Calendar.getInstance();//返回是Calendar的子类对象
//2.利用Iterator中的方法
boolean b = it.hasNext();//判断当前是否有要遍历的元素,有返回true,没有返回false
System.out.println(b);
Object obj = it.next();//取出当前遍历的元素
System.out.println(obj);//"李雷"
boolean b2 = it.hasNext();
System.out.println(b2);
Object obj2 = it.next();
System.out.println(obj2);//"韩梅梅"
boolean b3 = it.hasNext();
System.out.println(b3);
Object obj3 = it.next();
System.out.println(obj3);//"李梅"
boolean b4 = it.hasNext();
System.out.println(b4);//false,因为此时已经没有元素可以取
// Object obj4=it.next();
// System.out.println(obj4);//java.util.NoSuchElementException
}
}
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("李雷");
c.add("韩梅梅");
c.add("李梅");
Iterator it = c.iterator();//c.iterator()一定返回Iterator的实现类对象
//多态思想
//Calendar c=Calendar.getInstance();//返回是Calendar的子类对象
while (it.hasNext()) {//判断是否有元素
Object obj = it.next();//取出元素
System.out.println(obj);
}
}
并发修改异常
/*
* 需求:
* 定义一个集合,往集合中添加三个字符串,遍历集合
* 如果在遍历过程中发现有"abc",我们就向集合中再添加一个字符串"mm"
* Exception in thread "main" java.util.ConcurrentModificationException
* 并发修改异常:
* 产生原因:在使用迭代器遍历的时候,使用了集合的方法添加/删除 集合中元素
* 解决方案:使用普通for循环方式遍历集合,再往集合中添加
*/
//使用迭代器方式遍历
public static void main(String[] args) {
//1.定义一个集合
Collection c=new ArrayList();
//2.往集合中添加三个字符串
c.add("aa");
c.add("bc");
c.add("abc");
System.out.println(c);
//3.遍历集合判断集合中是否有abc,如果有我就往集合中添加一个字符串mm
Iterator it = c.iterator();
while(it.hasNext()){
Object obj=it.next();
if(obj.equals("abc")){
c.add("mm");
}
}
System.out.println(c);
}
//使用for循环遍历
public static void main(String[] args) {
//1.定义一个集合
Collection c=new ArrayList();
//2.往集合中添加三个字符串
c.add("aa");
c.add("bc");
c.add("abc");
System.out.println(c);//[aa, bc, abc]
//利用for循环遍历
for(int i=0;i<c.size();i++){
ArrayList al=(ArrayList)c;//对c进行向下转型
Object obj=al.get(i);//取出当前元素
if(obj.equals("abc")){
c.add("mm");
}
}
System.out.println(c);//[aa, bc, abc, mm]
}
增强for循环遍历
JDK1.5新特性:增强for,遍历数组或集合经常使用增强for
/*
* JDK1.5新特性:
* 增强for循环
* 格式:
* for(容器中元素类型 变量 : 数组对象或者集合对象){
* //通过for上声明的变量取值
* }
* 1.用普通for遍历数组有索引,而用增强for遍历数组没有索引
* 2.使用增强for遍历集合,需要集合实现或者继承Iterable接口
* 增强for遍历集合,其实底层使用依然是迭代器
* 3.增强for可以用快捷键生成 写一个for,alt+/
*/
public static void main(String[] args) {
Collection c=new ArrayList();
c.add("abc");//将"abc"以Object类型添加到集合,取出的时候也是Object,类似:Object obj="abc"
c.add("def");
for(Object obj : c){
System.out.println(obj);//第一次循环obj="abc"
//第二次循环obj="def"
}
}
泛型
概述
泛型:广泛的引用数据类型
泛型是jdk1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,使代码可以应用于多种类型。简单来说,java语言引入泛型的好处是安全简单,且所有强制类型转换都是自动和隐式进行的,提高了代码的重用率。
把对象的类型作为参数,指定到其它类或者方法上,从而保证类型转换的安全性和稳定性,这就是泛型。泛型的本质就是参数化类型。
语法格式:
类1或者接口<类型实参> 对象 = new 类2<类型实参>();
类上的泛型
泛型的引入
class GenericDemo01 {
public void method01(Integer i) {
System.out.println(i);
}
public void method02(String s) {
System.out.println(s);
}
}
public class Demo01 {
public static void main(String[] args) {
GenericDemo01 gd=new GenericDemo01();
gd.method01(3);
gd.method02("abc");
}
}
我们的method01和metho02的方法逻辑完全一样,只不过参数类型不同,而我们却写了2次
因此我们考虑能不能通过一个方法来使用不同的类型,使用泛型
类上泛型的使用
/*类上的泛型:
* 格式:
* class 类名<E,Q,..>{//E,Q泛型变量
* //再类中使用这些泛型变量
* }
* 使用:
* 创建对象的时候需要带上<>,然后在里面传入引用类型
* 创建对象的时候,泛型变量值确定
* 再类中用到这个泛型变量的地方都会被替换成传入的类型
*/
class GenericDemo<E>{//E是泛型变量
//E=String
public void method(E e){//String e
System.out.println(e);
}
}
public class Demo {
public static void main(String[] args) {
//由于类上有泛型,因此在创建对象的时候需要带上<>
GenericDemo<String> gd=new GenericDemo<String>();
//里面写要传过去的引用类型
gd.method("abc");
//由于类上有泛型,因此在创建对象的时候需要带上<>
GenericDemo<Integer> gd2=new GenericDemo<Integer>();
gd2.method(3);
}
}
集合类上使用泛型
ArrayList类上定义的有泛型,并且add方法就使用了ArrayList上的泛型E,因此我们按照泛型的用法使用
/*ArrayList定义:
* class ArrayList<E>{
* boolean add(E e){//使用的E就是ArrayList上的E
* }
* }
*/
import java.util.ArrayList;
public class Demo {
public static void main(String[] args) {
ArrayList<String> al=new ArrayList<String>();
al.add("abc");//由于创建ArrayList时候我们给泛型变量E传入String类型
//add方法上的E替换成String
al.add("def");
al.add("ghk");
System.out.println(al);//[abc, def, ghk]
}
}
方法上的泛型
方法上定义泛型
/*
* 方法上的泛型:
* 格式:
* class 类名{
* 修饰符 <T,E,...>返回值类型 方法名(T t,E e,...){
* }
* }
* 方法上的泛型,由传入的参数类型决定
*/
class GenericDemo {
public <T> void method(T t) {// T将来接收引用类型 t变量名
System.out.println(t);
}
}
public class Demo {
public static void main(String[] args) {
GenericDemo gd=new GenericDemo();
gd.method("abc");//当传入"abc"的时候,此时T被替换成String
gd.method(2);//当传入2的时候,此时T被替换成基本类型包装类Integer
}
}
集合中使用泛型的方法
只要给泛型方法传入什么类型,泛型变量就会被替换为什么类型
/*
*Collection中的方法使用到泛型:
* <T> T[] toArray(T[] a):将集合转换成数组,同时把集合中的元素添加到数组中
*/
import java.util.ArrayList;
import java.util.Collection;
public class Demo {
public static void main(String[] args) {
Collection<String> c=new ArrayList<String>();
c.add("abc");
c.add("def");
String[] strArr=new String[c.size()];
String[] arr=c.toArray(strArr);//当传入一个字符串数组的时候
//方法变为:<String> String[] toArray(String[] a)
//返回的数组中装满了集合的元素
for (String str : arr) {//打印数组的元素
System.out.println(str);//abc def
}
}
}
接口上的泛型
接口上定义泛型
/*
* 接口上的泛型定义格式:
* interface 接口名<E,Q,T,...>{
* //可以在接口中使用泛型变量
* }
*/
public interface Father<T>{
public void method(T t);//使用的是接口上的泛型T
}
- 接口上的泛型的第一种被确定方式:
/*
*确定接口上的泛型的第一种方式
* 当子类实现接口的时候传入类型,那么会把这个类型代替接口上声明的泛型变量
* 在接口中用到泛型变量的地方也会替换成该类型
* Father<String>->String替换了接口上的T
* method方法上的T也被替换成String
*/
public class Son implements Father<String> {
@Override
public void method(String t) {
System.out.println(t);
}
}
class Demo {
public static void main(String[] args) {
Son s=new Son();
s.method("abc");
}
}
- 接口上的泛型第二种被确定方式:
/*
*确定接口上的泛型的第二种方式
*当创建子类对象的时候来确定泛型的值
* Son2<Integer> s=new Son2<Integer>();
* 类上声明T会被替换成Integer
* method方法上的T也会被替换成Integer
*/
public class Son2<T> implements Father<T> {
public void method(T t) {
System.out.println(t);
}
}
class Demo {
public static void main(String[] args) {
Son2<Integer> s=new Son2<Integer>();
s.method(3);
}
}
迭代器以及增强for使用泛型
我们后期都会采用泛型的写法,无论是接口上的泛型还是类上的泛型
只要被确定了类型,那么所有用到泛型的地方都会被确定为这个类型
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*
*1.定义集合的时候使用泛型
*2.使用迭代器遍历的时候使用泛型
interface Collection<E>{
Iterator<E> iterator()//Iterator<E>中的E用的是Collection上的E
}
interface Iterator<E>{
boolean hasNext()
E next()//next()方法上用的E就是Iterator接口上的E
}
3.使用增强for遍历也使用泛型
*/
public class Demo {
public static void main(String[] args) {
//method01();
method02();
}
public static void method02(){
//使用泛型定义集合
Collection<String> c=new ArrayList<String>();
c.add("乔峰");//只能添加String类型,因为add方法的形参被限定为String
c.add("慕容复");
c.add("王语嫣");
for(String str : c){//增强for底层在遍历集合的时候其实用的还是迭代器
System.out.println(str.length());
}
}
//使用泛型的迭代器
public static void method01() {
//使用泛型定义集合
Collection<String> c=new ArrayList<String>();
c.add("乔峰");//只能添加String类型,因为add方法的形参被限定为String
c.add("慕容复");
c.add("王语嫣");
//获取迭代器对象
Iterator<String> it=c.iterator();
//使用Iterator中的方法进行遍历
while(it.hasNext()){
String str=it.next();
System.out.println(str.length());//使用泛型后,避免强转,可以直接使用存入的元素的方法
}
}
}