------
Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
泛型
泛型:JDK1.5版本以后出现的新特性。用于解决安全问题,是一个安全机制。
JDK1.5的集合类希望在定义集合时,明确表明你要向集合中装入那种类型的数据,无法加入指定类型以外的数据。
因为集合是用来存储对象的,没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。但是当我们往同一个集合中存储两个不同类型的数据时,编译的时候可能不会失败,当我们将产品使用的时候这些问题就会暴露出来,影响产品的使用。所以我们需要对某一个集合中的数据类型进行限定。
使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全,即使存入了非限定类型的数据,在我们编译的时候也能够将问题暴露出来,方便程序员修改完善。
并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。
泛型就是把原来的类名进行了延长!
格式: 通过<>来定义要操作的引用数据类型
如:ArrayList<Integer> //定义要存入集合中的元素指定为Integer类型
如:ArrayList<Integer> //定义要存入集合中的元素指定为Integer类型
其实<>就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数,传递到<>即可.
当使用集合时,将集合中要存储的数据类型作为参数,传递到<>即可.
好处:1、将运行时期出现的问题ClassCastException,转移到了编译时期,方便于程序员解决问题。让运行时期问题减少,安全。
2、避免了强制转换的麻烦。
2、避免了强制转换的麻烦。
在使用java提供的对象时,什么时候使用泛型呢?
通常在集合框架中很常见,只要见到<>就要定义泛型
通常在集合框架中很常见,只要见到<>就要定义泛型
来看一个泛型的简单应用:用上次的一个将TreeSet集合中的元素根据长度排序的例子来说明
import java.util.*;
class GenericDemo
{
public static void main(String[] args)
{
//使用比较器来排序,使用了泛型,确定了集合中元素的类型为String
TreeSet<String> ts=new TreeSet<String>(new myCompare());
//添加元素
ts.add("asdf");
ts.add("df");
ts.add("fasdfaf");
ts.add("dfs");
ts.add("efs");
//使用迭代器,这里同样要定义泛型
Iterator<String> it=ts.iterator();
while(it.hasNext())
{
String s=it.next(); //使用了泛型后,这里就不需要再类型强制转换了,因为集合中的元素已确定为String类型
sop(s);
}
}
//打印
public static void sop(Object obj)
{
System.out.println(obj);
}
}
class myCompare implements Comparator<String>
{
public int compare(String o1,String o2)
{
/*
if(!((o1 instanceof String)&&(o2 instanceof String)))
throw new RuntimeException("非字符串");
String s1=(String)o1;
String s2=(String)o2;*/
int num=new Integer(o1.length()).compareTo(o2.length());
if(num==0)
return o1.compareTo(o2);
return num;
}
}
从上面的代码里可以看到,我们在创建比较器时,实现Comparator接口时,也定义了要比较的数据类型为String,重写compare方法时,参数类型不再是Object了,而是String。在方法体中,注释的部分是使用泛型之前,需要判断参数类型,以及类型强制转换的部分。使用泛型后不需要了,因为这里要操作的数据类型已经明确为String类型。
一、泛型类
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展。
现在定义泛型来完成扩展。
现在定义泛型来完成扩展。
看一个代码段,来看看泛型的定义格式
import java.util.*;
class Worker
{
}
class Student
{
}
//这个就是泛型类
class Util<TT>
{
private TT t;
public void set(TT t)
{
this.t=t;
}
public TT get()
{
return t;
}
}
class GenericDemo1
{
public static void main(String[] args)
{
//这里这样定义,说明我要操作的引用型对象是Worker类型的
//这里泛型类的对象明确了要操作的类型Worker,那么要操作的类型就固定了。
//如果要操作其他对类型,那么只能再创建一个泛型类对象
Util<Worker> ut=new Util<Worker>();
//设置对象
ut.set(new Worker());
/*这里会编译失败:不兼容的类型:Student无法转换为Worker
如果Util中用的不是泛型,而是Object,这时编译成功,但是运行的时候报错。
这里用到泛型,是将问题在编译时期暴露出来,以便程序员修改,而不是在运行时才出现
提高了安全性*/
//ut.set(new Student());
//获取对象
Worker wo=ut.get();
}
}
二、泛型方法
为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。如下:
class GenericDemo2
{
public static void main(String[] args)
{
/*************泛型定义在方法上***************/
Demo d=new Demo();
d.show("我是字符串类型");
d.show(new Integer(4));//定义在方法中后,要使用什么类型就使用什么类型,方便
d.print(new Boolean(true));
d.print("asfd");
}
}
//将泛型定义在方法
class Demo
{
public <T> void show(T t)
{
System.out.println("show:"+t);
}
public <T> void print(T t)
{
System.out.println("print:"+t);
}
//方法中的<T> 即是定义一个类型,否则,JVM不认识参数列表中的T
//注意,这两个方法中的T 是不相同的。但在使用的时候也可以相同
}
结果:
三、泛型类中的泛型方法及静态泛型方法
class GenericDemo2
{
public static void main(String[] args)
{
/***********泛型定义在方法和类上********************/
Demo1<String> d1=new Demo1<String>();
//当泛型类要操作的类确定后,其内与类上定义的方法只能操作此类型的对象
d1.show("asdf"); //此方法只能操作String类型
//d1.show(4);//此处编译失败,因为show方法操作的类型和泛型类操作的引用数据类型一致
d1.print(4);//相当于new Integer(4),自动拆装箱
d1.print(new Boolean(false));
/*******静态泛型********/
Demo1.method(3);
Demo1.method("adf");
Demo1.method(new Boolean(true));
}
}
//将泛型定义在类中和方法上
class Demo1<T>
{
public void show(T t)
{
System.out.println("show:"+t);
}
//泛型方法
public <Q> void print(Q q)
{
System.out.println("print:"+q);
}
//静态泛型类
public static <M> void method(M m)
{
System.out.println("static method:"+m);
}
//注意,泛型类中的泛型方法和静态泛型方法所定义的类型不能和类定义的类型一样
//也就是说泛型类是<T>,泛型方法和静态泛型方法都不能是<T>,而且这两者之间也不能相同。
}
结果:
泛型接口
两种方式
一、实现接口时确定了要操作的数据类型
二、实现接口时不确定要操作的数据类型
用代码来演示:
//定义了泛型的接口
interface Inter<T>
{
void show(T t);
}
//实现接口时确定了要操作的数据类型
class InterImpl implements Inter<String>
{
public void show(String t)
{
System.out.println("show:"+t);
}
}
//实现接口时不确定要操作的数据类型
class InterImp<T> implements Inter<T>
{
public void show(T t)
{
System.out.println("show:"+t);
}
}
class GenericDemo3
{
public static void main(String[] args)
{
InterImpl ii=new InterImpl();
ii.show("asdf");
InterImp<String> it=new InterImp<String>();
it.show("asdfasf");
InterImp<Integer> it1=new InterImp<Integer>();
it1.show(5);
}
}
结果:
泛型限定
? 通配符 。也可以理解为占位符
通过代码来理解通配符的使用
import java.util.*;
class GenericDemo4
{
public static void main(String[] args)
{
ArrayList<String> al=new ArrayList<String>();
al.add("abc01");
al.add("abc02");
al.add("abc03");
ArrayList<Integer> al1=new ArrayList<Integer>();
al1.add(4);
al1.add(5);
al1.add(6);
//通过泛型,我们将创建迭代器的方法封装,不用每次创建迭代器来获取集合中的元素
printColl(al);
printColl(al1);
printColl1(al);
printColl1(al1);
}
//通配符
public static void printColl(ArrayList<?> al)
{
Iterator<?> it=al.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
//泛型
public static <T> void printColl1(ArrayList<T> al)
{
Iterator<T> it=al.iterator();
while(it.hasNext())
{
T t=it.next();
System.out.println(t);
}
}
}
结果:
从上例中可以看出,在这里使用通配符和使用泛型的差别不大。
对于一个范围内的一类事物,可以通过泛型限定的方式定义,有两种方式:
1、? extends E:可接收E类型或E类型的子类型;称之为上限。
如:ArrayList<? extends Person>x = new ArrayList<Student>();
2、? super E:可接收E类型或E类型的父类型;称之为下限。
如:ArrayList<? super Student>x = new ArrayList<Person>();
1、? extends E:可接收E类型或E类型的子类型;称之为上限。
如:ArrayList<? extends Person>x = new ArrayList<Student>();
2、? super E:可接收E类型或E类型的父类型;称之为下限。
如:ArrayList<? super Student>x = new ArrayList<Person>();
如下代码:
结果如图:
import java.util.*;
class GenericDemo5
{
public static void main(String[] args)
{
//父类Person对象的集合
ArrayList<Person> al=new ArrayList<Person>();
al.add(new Person("abc1"));
al.add(new Person("abc2"));
al.add(new Person("abc3"));
//打印集合中的元素
printColl(al);
//子类Student对象
ArrayList<Student> al1=new ArrayList<Student>();
al1.add(new Student("abc001"));
al1.add(new Student("abc002"));
al1.add(new Student("abc003"));
//打印子类对象的集合中的元素
printColl(al1);
}
//向上限定,参数可以Person和Person的子类对象的集合
public static void printColl(ArrayList<? extends Person> al)
{
Iterator<? extends Person> it=al.iterator();
while(it.hasNext())
{
System.out.println(it.next().getName());
}
}
}
//父类
class Person
{
private String name;
Person(String name)
{
this.name=name;
}
public String getName()
{
return this.name;
}
}
//子类
class Student extends Person
{
Student(String name)
{
super(name);
}
}
结果如图:
Map
Map集合:该集合存储键值对,一对一对往里存。而且要保证键的唯一性。
接口 Map<K,V>
其中,K表示键key的数据类型,而V表示值value的数据类型
Map
|--HashMap:底层是哈希表数据结构,允许使用null键null值,该集合是不同步的。 jdk1.2 高效
|--TreeMap:底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。
常用方法
1、添加
v put(key,value)
v put(key,value)
putAll()
2、删除
2、删除
clear()
remove(Object key)
3、判断
containsValue(Object value)
containsKey(Object key)
isEmpty()
4、获取
get(Object key):返回指定键所映射的值
size():
Collection<v> values():返回此映射中包含的值的Collection集合
来看看这些方法的使用
remove(Object key)
3、判断
containsValue(Object value)
containsKey(Object key)
isEmpty()
4、获取
get(Object key):返回指定键所映射的值
size():
Collection<v> values():返回此映射中包含的值的Collection集合
来看看这些方法的使用
import java.util.*;
class MapDemo
{
public static void main(String[] args)
{
Map<String,String> map=new HashMap<String,String>();
//添加元素
map.put("01","zhangsan1");
map.put("02","zhangsan2");
map.put("03","zhangsan3");
//判断
sop(map.containsKey("02"));
sop(map.get("03"));
//sop();
//删除
map.remove("01");
map.put(null,"zs");
map.put("04",null);
sop(map.get("04"));//可以通过get方法的返回值判断一个键是否存在。
//获取map集合中的所有值
Collection<String> coll=map.values();
sop(coll);
sop(map);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
Map集合的取出原理:将map集合转成Set集合。再通过迭代器取出。
map集合的两种取出方式:
1、keySet<K>:将map中所有的键存入到Set集合。因为set具备条件迭代器。所以可以迭代方式取出所有的键,再根据get方法。
获取每一个键对应的值
1、keySet<K>:将map中所有的键存入到Set集合。因为set具备条件迭代器。所以可以迭代方式取出所有的键,再根据get方法。
获取每一个键对应的值
如代码:
import java.util.*;
class MapDemo1
{
public static void main(String[] args)
{
//创建一个map集合,并添加元素。
Map<String,String> mapList=new HashMap<String,String>();
mapList.put("01","lisi1");
mapList.put("02","lisi2");
mapList.put("03","lisi3");
mapList.put("04","lisi4");
method_KeySet(mapList);
}
public static void method_KeySet(Map<String,String> map)
{
//将map集合转成Set集合。
Set<String> hs=map.keySet();
//创建Set的迭代器,获取SET集合中的元素
Iterator<String> it=hs.iterator();
while(it.hasNext())
{
String key=it.next();
//根据取出的键,通过Map中的get方法取出对应的值
String value=map.get(key);
sop("key:"+key+":value:"+value);
}
}
//打印
public static void sop(Object obj)
{
System.out.println(obj);
}
}
map集合的第二种取出方式
Set<Map.Entry<K,V>> entrySet:将map集合中的映射关系存入到了set集合中,而这个关系的数据类型就是Map.Entry
Set<Map.Entry<K,V>> entrySet:将map集合中的映射关系存入到了set集合中,而这个关系的数据类型就是Map.Entry
代码:
import java.util.*;
class MapDemo2
{
public static void main(String[] args)
{
//创建Map集合并添加元素
Map<String,String> mapList=new HashMap<String,String>();
mapList.put("01","lisi1");
mapList.put("02","lisi2");
mapList.put("03","lisi3");
mapList.put("04","lisi4");
getElements(mapList);
}
//第二种方式获取集合中的元素
public static void getElements(Map<String,String> map)
{
//使用entrySet 方法将映射关系存储到Set 集合中
Set<Map.Entry<String,String>> entrySet=map.entrySet();
//通过迭代器获取集合中的元素
Iterator<Map.Entry<String,String>> it=entrySet.iterator();
while(it.hasNext())
{
Map.Entry<String,String> me=it.next();
String key=me.getKey();//获取映射关系中的键
String value=me.getValue(); //获取映射关系中的值
sop("key:"+key+"....value:"+value);
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
看下面几个小练习:
/*
每个学生都有对应的归属地。
学生Student,地址String.
学生属性:姓名,年龄
注意:姓名和年龄相同的视为同一个学生。
保证学生的唯一性.
*/
import java.util.*;
class MapTest
{
public static void main(String[] args)
{
HashMap<Student,String> hm=new HashMap<Student ,String>();
hm.put(new Student("zhangsan01",20),"beijing");
hm.put(new Student("zhangsan02",21),"yunnan");
hm.put(new Student("zhangsan03",22),"tianjing");
hm.put(new Student("zhangsan02",21),"shanghai");
/*第一种取出方式***********/
/*
Set<Student> keyset=hm.keySet();
Iterator<Student> it=keyset.iterator();
while(it.hasNext())
{
Object obj=it.next();
Student s=(Student)obj;
String value=hm.get(obj);
sop(s.getName()+":::"+s.getAge()+"::"+value);
}
*/
/**第二种取出方式********/
Set<Map.Entry<Student,String>> entryset=hm.entrySet();
Iterator<Map.Entry<Student,String>> it=entryset.iterator();
while(it.hasNext())
{
Map.Entry<Student,String> me=it.next();
Student s=me.getKey();
String adr=me.getValue();
sop(s.getName()+"::"+s.getAge()+"..."+adr);
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
class Student implements Comparable<Student>
{
private String name;
private int age;
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public int compareTo(Student s)
{
int num=new Integer(this.age).compareTo(s.age);
if(num==0)
return this.name.compareTo(s.name);
return num;
}
Student(String name,int age)
{
this.name=name;
this.age=age;
}
//复写hashCode方法
public int hashCode()
{
/*
if(!(obj instanceof Student))
throw new ClassCastException("添加的不是Student对象");
Student s=(Student)obj;*/
return this.name.hashCode()+this.age*29;
}
//复写equals方法
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw new ClassCastException("添加的不是Student对象");
Student s=(Student)obj;
return this.name.equals(s.name)&&(this.age==s.age);
}
}
结果:
再看下一个练习,这个是上面练习的需要中添加了排序功能
/*
每个学生都有对应的归属地。
学生Student,地址String.
学生属性:姓名,年龄
注意:姓名和年龄相同的视为同一个学生。
保证学生的唯一性.
需求:对学生对象的年龄进行升序排序
因为是键值对,而且要排序,所以用TreeMap
*/
import java.util.*;
class MapTest1
{
public static void main(String[] args)
{
TreeMap<Student,String> tm=new TreeMap<Student,String>(new mycomparator());
tm.put(new Student("zhangsan01",22),"wuhan");
tm.put(new Student("zhangsan02",22),"xizang");
tm.put(new Student("zhangsan03",25),"nanjing");
tm.put(new Student("zhangsan02",22),"hebei");
tm.put(new Student("zhangsan04",19),"taiyuan");
//获取集合中的元素
Set<Student> keyset=tm.keySet();
//使用迭代器获取集合元素
Iterator<Student> it=keyset.iterator();
while(it.hasNext())
{
Student s=it.next();
String adr=tm.get(s);
sop(s.getName()+":::"+s.getAge()+"::::::::"+adr);
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
//新建一个比较器
class mycomparator implements Comparator<Student>
{
//复写compare方法
public int compare(Student s1,Student s2)
{
int num=(new Integer(s1.getAge())).compareTo(new Integer(s2.getAge()));
if(num==0) //当主要条件相等时,要判断次要条件
return s1.getName().compareTo(s2.getName());
return num;
}
}
结果:
从结果中可以看出,当向Map集合中插入两个相同元素时,即key相同时,后面的那个会覆盖掉key原来对应的value值,并将此value值返回