【Java】Collection集合&泛型(二)

简介: 本期主要介绍Collection集合&泛型

3.2 使用泛型的好处


上一节只是讲解了泛型的引入,那么泛型带来了哪些好处呢?

  • 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
  • 避免了类型强转的麻烦。

通过我们如下代码体验一下:

publicclassGenericDemo2 {
publicstaticvoidmain(String[] args) {
Collection<String>list=newArrayList<String>();
list.add("abc");
list.add("itcast");
// list.add(5);//当集合明确类型后,存放类型不一致就会编译报错// 集合已经明确具体存放的元素类型,那么在使用迭代器的时候,迭代器也同样会知道具体遍历元素类型Iterator<String>it=list.iterator();
while(it.hasNext()){
Stringstr=it.next();
//当使用Iterator<String>控制元素类型后,就不需要强转了。获取到的元素直接就是String类型System.out.println(str.length());
        }
    }
}

tips:泛型是数据类型的一部分,我们将类名与泛型合并一起看做数据类型。

3.3 泛型的定义与使用


我们在集合中会大量使用到泛型,这里来完整地学习泛型知识。

泛型,用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。

定义和使用含有泛型的类


定义格式:

修饰符 class 类名<代表泛型的变量> {  }

例如,API中的ArrayList集合:

classArrayList<E>{ 
publicbooleanadd(Ee){ }
publicEget(intindex){ }
    ....
}

使用泛型: 即什么时候确定泛型。

在创建对象的时候确定泛型

例如,ArrayList<String> list = new ArrayList<String>();

此时,变量E的值就是String类型,那么我们的类型就可以理解为:

classArrayList<String>{ 
publicbooleanadd(Stringe){ }
publicStringget(intindex){  }
     ...
}

再例如,ArrayList<Integer> list = new ArrayList<Integer>();

此时,变量E的值就是Integer类型,那么我们的类型就可以理解为:

classArrayList<Integer> { 
publicbooleanadd(Integere) { }
publicIntegerget(intindex) {  }
     ...
}

举例自定义泛型类

publicclassMyGenericClass<MVP> {
//没有MVP类型,在这里代表 未知的一种数据类型 未来传递什么就是什么类型privateMVPmvp;
publicvoidsetMVP(MVPmvp) {
this.mvp=mvp;
    }
publicMVPgetMVP() {
returnmvp;
    }
}

使用:

publicclassGenericClassDemo {
publicstaticvoidmain(String[] args) {         
// 创建一个泛型为String的类MyGenericClass<String>my=newMyGenericClass<String>();      
// 调用setMVPmy.setMVP("大胡子登登");
// 调用getMVPStringmvp=my.getMVP();
System.out.println(mvp);
//创建一个泛型为Integer的类MyGenericClass<Integer>my2=newMyGenericClass<Integer>(); 
my2.setMVP(123);         
Integermvp2=my2.getMVP();
    }
}

含有泛型的方法


定义格式:

修饰符 <代表泛型的变量> 返回值类型 方法名(参数){  }

例如,

publicclassMyGenericMethod {    
public<MVP>voidshow(MVPmvp) {
System.out.println(mvp.getClass());
    }
public<MVP>MVPshow2(MVPmvp) {   
returnmvp;
    }
}

使用格式:调用方法时,确定泛型的类型

publicclassGenericMethodDemo {
publicstaticvoidmain(String[] args) {
// 创建对象MyGenericMethodmm=newMyGenericMethod();
// 演示看方法提示mm.show("aaa");
mm.show(123);
mm.show(12.45);
    }
}

含有泛型的接口


定义格式:

修饰符 interface接口名<代表泛型的变量> {  }

例如,

publicinterfaceMyGenericInterface<E>{
publicabstractvoidadd(Ee);
publicabstractEgetE();  
}

使用格式:

1、定义类时确定泛型的类型

例如

publicclassMyImp1implementsMyGenericInterface<String> {
@Overridepublicvoidadd(Stringe) {
// 省略...    }
@OverridepublicStringgetE() {
returnnull;
    }
}

此时,泛型E的值就是String类型。

2、始终不确定泛型的类型,直到创建对象时,确定泛型的类型

例如

publicclassMyImp2<E>implementsMyGenericInterface<E> {
@Overridepublicvoidadd(Ee) {
// 省略...    }
@OverridepublicEgetE() {
returnnull;
    }
}
//确定泛型:/** 使用*/publicclassGenericInterface {
publicstaticvoidmain(String[] args) {
MyImp2<String>my=newMyImp2<String>();  
my.add("aa");
    }
}

3.4 泛型通配符


当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object

类中的共性方法,集合中元素自身方法无法使用。

通配符基本使用

泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。

此时只能接受数据,不能往该集合中存储数据。

举个例子大家理解使用即可:

publicstaticvoidmain(String[] args) {
Collection<Intger>list1=newArrayList<Integer>();
getElement(list1);
Collection<String>list2=newArrayList<String>();
getElement(list2);
}
publicstaticvoidgetElement(Collection<?>coll){}
//?代表可以接收任意类型

tips:泛型不存在继承关系 Collection<Object> list = new ArrayList<String>();这种是错误的。

通配符高级使用----受限泛型

之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限下限

泛型的上限

  • 格式类型名称 <? extends 类 > 对象名称
  • 意义只能接收该类型及其子类

泛型的下限

  • 格式类型名称 <? super 类 > 对象名称
  • 意义只能接收该类型及其父类型

比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类

publicstaticvoidmain(String[] args) {
Collection<Integer>list1=newArrayList<Integer>();
Collection<String>list2=newArrayList<String>();
Collection<Number>list3=newArrayList<Number>();
Collection<Object>list4=newArrayList<Object>();
getElement(list1);
getElement(list2);//报错getElement(list3);
getElement(list4);//报错getElement2(list1);//报错getElement2(list2);//报错getElement2(list3);
getElement2(list4);
}
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类publicstaticvoidgetElement1(Collection<?extendsNumber>coll){}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类publicstaticvoidgetElement2(Collection<?superNumber>coll){}

第四章 集合综合案例


4.1 案例介绍


按照斗地主的规则,完成洗牌发牌的动作。 具体规则:

使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。

4.2 案例分析


  • 准备牌:
  • 牌可以设计为一个ArrayList<String>,每个字符串为一张牌。 每张牌由花色数字两部分组成,我们可以使用花色集合与数字集合嵌套迭代完成每张牌的组装。 牌由Collections类的shuffle方法进行随机排序。
  • 发牌将每个人以及底牌设计为ArrayList<String>,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。
  • 看牌
    直接打印每个集合。

4.3 代码实现


importjava.util.ArrayList;
importjava.util.Collections;
publicclassPoker {
publicstaticvoidmain(String[] args) {
/** 1: 准备牌操作*///1.1 创建牌盒 将来存储牌面的 ArrayList<String>pokerBox=newArrayList<String>();
//1.2 创建花色集合ArrayList<String>colors=newArrayList<String>();
//1.3 创建数字集合ArrayList<String>numbers=newArrayList<String>();
//1.4 分别给花色 以及 数字集合添加元素colors.add("♥");
colors.add("♦");
colors.add("♠");
colors.add("♣");
for(inti=2;i<=10;i++){
numbers.add(i+"");
        }
numbers.add("J");
numbers.add("Q");
numbers.add("K");
numbers.add("A");
//1.5 创造牌  拼接牌操作// 拿出每一个花色  然后跟每一个数字 进行结合  存储到牌盒中for (Stringcolor : colors) {
//color每一个花色 //遍历数字集合for(Stringnumber : numbers){
//结合Stringcard=color+number;
//存储到牌盒中pokerBox.add(card);
            }
        }
//1.6大王小王pokerBox.add("小☺");
pokerBox.add("大☠");   
// System.out.println(pokerBox);//洗牌 是不是就是将  牌盒中 牌的索引打乱 // Collections类  工具类  都是 静态方法// shuffer方法   /** static void shuffle(List<?> list) *     使用默认随机源对指定列表进行置换。 *///2:洗牌Collections.shuffle(pokerBox);
//3 发牌//3.1 创建 三个 玩家集合  创建一个底牌集合ArrayList<String>player1=newArrayList<String>();
ArrayList<String>player2=newArrayList<String>();
ArrayList<String>player3=newArrayList<String>();
ArrayList<String>dipai=newArrayList<String>();    
//遍历 牌盒  必须知道索引   for(inti=0;i<pokerBox.size();i++){
//获取 牌面Stringcard=pokerBox.get(i);
//留出三张底牌 存到 底牌集合中if(i>=51){//存到底牌集合中dipai.add(card);
            } else {
//玩家1   %3  ==0if(i%3==0){
player1.add(card);
                }elseif(i%3==1){//玩家2player2.add(card);
                }else{//玩家3player3.add(card);
                }
            }
        }
//看看System.out.println("令狐冲:"+player1);
System.out.println("田伯光:"+player2);
System.out.println("绿竹翁:"+player3);
System.out.println("底牌:"+dipai);  
    }
}
相关文章
|
6月前
|
Java 大数据 API
Java Stream API:现代集合处理与函数式编程
Java Stream API:现代集合处理与函数式编程
347 100
|
6月前
|
Java API 数据处理
Java Stream API:现代集合处理新方式
Java Stream API:现代集合处理新方式
355 101
|
6月前
|
算法 Java
50道java集合面试题
50道 java 集合面试题
|
5月前
|
存储 算法 安全
Java集合框架:理解类型多样性与限制
总之,在 Java 题材中正确地应对多样化与约束条件要求开发人员深入理解面向对象原则、范式编程思想以及JVM工作机理等核心知识点。通过精心设计与周密规划能够有效地利用 Java 高级特征打造出既健壮又灵活易维护系统软件产品。
161 7
|
6月前
|
安全 Java
Java之泛型使用教程
Java之泛型使用教程
428 10
|
7月前
|
存储 缓存 安全
Java集合框架(二):Set接口与哈希表原理
本文深入解析Java中Set集合的工作原理及其实现机制,涵盖HashSet、LinkedHashSet和TreeSet三大实现类。从Set接口的特性出发,对比List理解去重机制,并详解哈希表原理、hashCode与equals方法的作用。进一步剖析HashSet的底层HashMap实现、LinkedHashSet的双向链表维护顺序特性,以及TreeSet基于红黑树的排序功能。文章还包含性能对比、自定义对象去重、集合运算实战和线程安全方案,帮助读者全面掌握Set的应用与选择策略。
673 23
|
6月前
|
存储 Java Go
对比Java学习Go——函数、集合和OOP
Go语言的函数支持声明与调用,具备多返回值、命名返回值等特性,结合`func`关键字与类型后置语法,使函数定义简洁直观。函数可作为一等公民传递、赋值或作为参数,支持匿名函数与闭包。Go通过组合与接口实现面向对象编程,结构体定义数据,方法定义行为,接口实现多态,体现了Go语言的简洁与高效设计。
181 4
|
7月前
|
存储 缓存 安全
Java集合框架(三):Map体系与ConcurrentHashMap
本文深入解析Java中Map接口体系及其实现类,包括HashMap、ConcurrentHashMap等的工作原理与线程安全机制。内容涵盖哈希冲突解决、扩容策略、并发优化,以及不同Map实现的适用场景,助你掌握高并发编程核心技巧。
|
7月前
|
存储 NoSQL Java
Java Stream API:集合操作与并行处理
Stream API 是 Java 8 提供的集合处理工具,通过声明式编程简化数据操作。它支持链式调用、延迟执行和并行处理,能够高效实现过滤、转换、聚合等操作,提升代码可读性和性能。
|
7月前
|
存储 安全 Java
Java集合框架(一):List接口及其实现类剖析
本文深入解析Java中List集合的实现原理,涵盖ArrayList的动态数组机制、LinkedList的链表结构、Vector与Stack的线程安全性及其不推荐使用的原因,对比了不同实现的性能与适用场景,帮助开发者根据实际需求选择合适的List实现。