黑马程序员——集合:泛型和新特性

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

泛型

泛型的作用

更安全:

将运行时期出现的问题ClassCastException,转移到了编译时期。方便于程序员解决问题。让运行时期问题减少、安全。
import java.util.*;
class GenericDemo
{
	public static void main(String[] args) 
	{
	//初始化部分
		TreeSet ts = new TreeSet(new LenComparator());//使用泛型
		ts.add("abcd");
		ts.add("cc");
		ts.add("cba");
		ts.add("aaa");
		ts.add("z");
		ts.add("hahaha");
		
	//遍历输出部分
		Iterator it = ts.iterator();//使用泛型
		while(it.hasNext())
		{
			String s = it.next();
			System.out.println(s);
		}
		
	}
}

class LenComparator implements Comparator//使用泛型
{
	public int compare(String o1,String o2)
	{
		int num = new Integer(o2.length()).compareTo(new Integer(o1.length()));
		//对字符串长度进行比较,Integer的compareTo方法,A.compareTo(B),AB返回正数,A=B返回0
			//比如此处obj2:"cc" < obj1:"abcd",返回负数,obj1被存到红黑树的小端。输出的时候是先输出红黑树的小端,所以abcd比cc先输出
		//如果改变一下位置,变成 new Integer(o1.length()).compareTo(new Integer(o2.length()))
			//则此处 obj1:"abcd" > obj2:"cc",返回正数,obj1被存到红黑树的大端。输出的时候是先输出红黑树的小端,所以cc比abcd先输出
		if(num==0)
			return o2.compareTo(o1);
		return num;
	}
}

更简洁:

避免了强制转换的麻烦。如实现某接口时,指定传入接口方法的实参的类型,在复写该接口方法时就可以直接使用指定类型,而无需强制转换。

//代码比较

//迭代器
	//不使用泛型
		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			Student stu = (Student)it.next();
			System.out.println(stu.getName()+"..."+stu.getAge());
		}
	
	//使用泛型
		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			String s = it.next();
			System.out.println(s);
		}

//Comparator
	//不使用泛型
		class MyCompare implements Comparator
		{
			public int compare(Object o1,Object o2)//类型提升了,向上转型
			{
				Student s1 = (Student)o1;//所以为了获取到Student类本身的方法,还需要向下转型
				Student s2 = (Student)o2;
				
				int num = s1.getName().compareTo(s2.getName());
				if(num==0)
				{
					return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
				}		
				return num;
			}
	//使用泛型
		class LenComparator implements Comparator
		{
			public int compare(String o1,String o2)//传的时候就是String,没有向上转型的过程,就不需要再向下转型
			{
				int num = new Integer(o2.length()).compareTo(new Integer(o1.length()));
				if(num==0)
					return o2.compareTo(o1);
				return num;
			}

自定义带泛型的类

什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候可定义泛型类(早期定义Object来完成扩展。现在定义泛型来完成扩展)
class Worker
{

}
class Students
{
}
//泛型前做法。
class Tool//该作为工具的类可以同时提供给Worker和Students进行操作
{
	private Object obj;
	public void setObject(Object obj)
	{
		this.obj = obj;
	}
	public Object getObject()
	{
		return obj;
	}
}

//泛型类
class Utils//名称都是自己取的,Utils是类的名称,便是带泛型的形式,用来接收类参数
//用户在使用Utils类时,先要告诉Utils,你要把Utils提供的方法使用在什么类上,也就是说,你要把类的类型先传给Utils后才能使用Utils中的方法
//传入类的类型之后,如果你在使用Utils的方法时传入了错误的类,则在编译时期就会报错
{
	private QQ q;
	public void setObject(QQ q)
	{
		this.q = q;
	}
	public QQ getObject()
	{
		return q;
	}
}

class  GenericDemo3
{
	public static void main(String[] args) 
	{

		Utils u = new Utils();//必须先把类的类型作为参数传递给该自定义的泛型类Utils
	//使用Utils
		//u.setObject(new Students());此时如果你传的不是Worker
		
		Worker w = u.getObject();;
		
		/* 不使用泛型类时会发生的问题:
			Tool t = new Tool();
			t.setObject(new Student());
			Worker w = (Worker)t.getObject();
			//由于Tool类在不用泛型时传入的是上帝类Object,设置的时候设置的是学生,而获取的时候获取是Worker在编译时是不会发生问题的
				//但是在运行的时候会发生ClassCastException
		*/
	}
}

自定义带泛型的方法

什么时候定义泛型方法?
为了让方法可以操作不同类型,而且类型还不确定时,可以将泛型定义在方法上。
Colletion里面就是把方法定义成泛型方法
class Demo
{
	public   void show(T t)//将泛型定义在方法上,这样该方法可以把println构造函数内可以传入的类型都传入,扩展了应用性
	{
		System.out.println("show:"+t);
	}
	public  void print(Q q)
	{
		System.out.println("print:"+q);
	}
}

class GenericDemo4 
{
	public static void main(String[] args) 
	{
		
		Demo d = new Demo();
		//泛型定义在方法上,该方法可以操作任意类型
		//泛型定义在类上,一旦类型确定,那么接下来方法操作的都是那个被确定的类型
		d.show("haha");//输出:show:haha
		d.show(new Integer(4));//输出:show:4
		d.print("heihei");//输出:print:heihei
		d.print(3 > 4);//输出:print:false
		
		/*
		//对比:泛型定义在类上的情况
		 * //泛型类定义的泛型,在整个类中有效。如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。

			class Demo
			{
				public void show(T t)
				{
					System.out.println("show:"+t);
				}
				public void print(T t)
				{
					System.out.println("show:"+t);
				}
			
			}


			Demo d = new Demo();//对象一建立,要操作的类型就确定了,这里Integer或者String等是必须要指定的
	
			d.show(new Integer(4));//编译通过
			d.print("hah");//编译失败,因为已经规定需要操作的是Integer类
		
		*/
		
		
	}
}
既在类上定义泛型,又在方法上定义泛型的情况:

class Demo
{
	public void show(T t)//方法上没有泛型
	{
		System.out.println("show:"+t);
	}
	public  void print(Q q)
	{
		System.out.println("print:"+q);
	}
}

class GenericDemo4 
{
	public static void main(String[] args) 
	{
		
		Demo d = new Demo();//如果在这里又同时限定了String类型(不限定时以下都能正常输出)
		d.show("haha");//输出:show:haha
		d.show(new Integer(4));//编译不通过,因为show已经制定需要操作String类型
		d.print("heihei");//输出:print:heihei,print是一个泛型方法,即使Demo d = new Demo();还是可以正常使用
		d.print(3 > 4);//输出:print:false
	}
}
泛型方法应用在静态方法时的情况:
class Demo//这里的T要在建立对象时才被明确
{

	public  void print(Q q)
	{
		System.out.println("print:" + q);
	}
	
	public static  void method(W w)//静态被先加载,其后才有对象
	//静态方法不可以访问类上定义的泛型。
		//如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
		//如果不在void前加,写成public static void method(W w),编译失败:提示Cannot make a static reference to the non-static type T
	{
		System.out.println("method:"+ w);
	}
}

class GenericDemo4 
{
	public static void main(String[] args) 
	{
		Demo d = new Demo();
		d.print("heihei");
		d.method(34);
	}
}

自定义带泛型的接口

interface Inter
{
	void show(T t);
}

/*
常规的实现泛型接口的方法
	class InterImpl implements Inter
	{
		public void show(String t)
		{
			System.out.println("show :"+t);
		}
	}
*/

//实现带泛型的接口时,也不知道需要传入并操作的是什么类型的情况
	class InterImpl implements Inter
	{
		public void show(T t)
		{
			System.out.println("show :"+t);
		}
	}

class GenericDemo5 
{
	public static void main(String[] args) 
	{
		
		InterImpl i = new InterImpl();
		i.show(4);
		//InterImpl i = new InterImpl();
		//i.show("haha");
	}
}

泛型限定

? extends E: 可以接收E类型或者E的子类型。上限。
? super E: 可以接收E类型或者E的父类型。下限
? 通配符。也可以理解为占位符。

泛型限定产生的原因是什么?
import java.util.*;

class  GenericDemo6
{
	public static void main(String[] args) 
	{

		ArrayList al = new ArrayList();//ArrayList al 限定了String类型

		al.add("abc1");
		al.add("abc2");
		al.add("abc3");

		ArrayList al1 = new ArrayList();//ArrayList al1 限定了Integer类型
		al1.add(4);
		al1.add(7);
		al1.add(1);

		printColl(al);
		//printColl(al1);如果想要打印al1,Integer类型中的元素,还非得要写两个Iterator,Iterator 和Iterator来实现
		//如果使用了泛型限定,就可以避免出现这样繁琐的情况,简化了代码量,同时也提高了扩展性


	}
	public static void printColl(ArrayList al)
	{
		Iterator it = al.iterator();
		while(it.hasNext())
		{
			System.out.println(it.next());
		}
	}
}


使用占位符
import java.util.*;

class  GenericDemo6
{
	public static void main(String[] args) 
	{

		ArrayList al = new ArrayList();//ArrayList al 限定了String类型
		al.add("abc1");
		al.add("abc2");
		al.add("abc3");

		ArrayList al1 = new ArrayList();//ArrayList al1 限定了Integer类型
		al1.add(4);
		al1.add(7);
		al1.add(1);

		printColl(al);
		printColl(al1);
		/*
		 输出结果:
		 	abc1
			abc2
			abc3
			4
			7
			1
		 */
	}
	public static void printColl(ArrayList al)//比较:ArrayList,或者使用public static  void printColl(ArrayList al)

	{
		Iterator it = al.iterator();//比较:Iterator ,或者使用Iterator,区别是如果是这种方法下的T还可以在while内进行操作
		while(it.hasNext())
		{
			System.out.println(it.next());
		}
	}
}


只想打印一个类型范围,比如只打印Person类和它的子类
在没有进行泛型限定时所发生的情况:
import java.util.*;
class  GenericDemo6
{
	public static void main(String[] args) 
	{
		ArrayList al = new ArrayList();
		al.add(new Person("abc1"));
		al.add(new Person("abc2"));
		al.add(new Person("abc3"));
		printColl(al);

		ArrayList al1 = new ArrayList();
		al1.add(new Student("abc--1"));
		al1.add(new Student("abc--2"));
		al1.add(new Student("abc--3"));
		printColl(al1);  //此处将会编译失败,相当于ArrayList al = new ArrayList();error
							//Student里面只能存Student,不能存其他Person类的子类实例
	}
	public static void printColl(ArrayList al)//注意:此处的Iterator中是Person
	{
		Iterator it = al.iterator();//注意:此处的Iterator中是Person
		while(it.hasNext())
		{
			System.out.println(it.next());
		}
	}
}

class Person
{
	private String name;
	Person(String name)
	{
		this.name = name;
	}
	public String getName()
	{
		return name;
	}
}

class Student extends Person
{
	Student(String name)
	{
		super(name);
	}
}

使用了泛型限定后的核心代码示例1:
	public static void printColl(ArrayList al)//注意:此处使用了泛型限定
	{
		Iterator it = al.iterator();//注意:此处使用了泛型限定
		while(it.hasNext())
		{
			System.out.println(it.next().getName());
		}
	}
使用了泛型限定后的核心代码示例2:
class Comp implements Comparator//TreeSet构造器已经对此进行了泛型限定,可以接收Student以及其父类,如Person
//简单点说:在实现比较器的时候,如果类与类之间存在继承关系,那么只要把父类作为泛型参数进行实现,则它的子类都可以调用该比较器进行排序。但是局限性是只能用父类的方法
class Comp implements Comparator
{
	public int compare(Student s1,Student s2)//public int compare(Person s1,Person s2)
	{
		//Person s1 = new Student("abc1");传Student及其父类都可以,因为这两个类型都可以接收Student对象。关键是要能接收进行比较的子类对象
		return s1.getName().compareTo(s2.getName());
	}
}

TreeSet ts = new TreeSet(new Comp());
ts.add(new Student("abc1"));
ts.add(new Student("abc2"));
ts.add(new Student("abc3"));

新特性

增强for循环

1、格式:
for(数据类型 变量名:被遍历的集合(collection)或者数组) {执行语句}
2、说明
a、对集合进行遍历。只能获取集合元素。但是不能对集合进行操作。可以看作是迭代器的简写形式。
b、迭代器除了遍历,还可以进行remove集合中元素的动作。如果使用ListIterator,还可以在遍历过程中对集合进行增删改查的操作。
3、与传统for的区别:
高级for有一个局限性。必须有被遍历的目标(集合或数组)。传统for遍历数组时有索引。在遍历数组的时候,建议还是使用传统for。因为传统for可以定义角标。

class newChar 
{
	public static void main(String[] args) 
	{
	//用高级for遍历集合
		//ArrayList al的初始化
			ArrayList al = new ArrayList();
			al.add("abc1");
			al.add("abc2");
			al.add("abc3");
	
		//增强型for循环
			for(String s : al)//前面不加泛型,不能这么用
		  //for(数据类型 变量名:被遍历的数组或集合)
			{
				//s = "kk";
				System.out.println("s:" + s);
				/*
				 输出结果:
				 	s:abc1
					s:abc2
					s:abc3
				 */
			}
	
			System.out.println(al);//输出结果:[abc1, abc2, abc3]
			
			/*使用迭代器时的情况
				Iterator it = al.iterator();
				while(it.hasNext())
				{
					System.out.println(it.next());
				}
			*/
	//用高级for遍历数组
		//数组初始化
			int[] arr = {3,5,1};
		//传统for循环遍历
			for(int x=0; x hm = new HashMap();
			hm.put(1,"a");
			hm.put(2,"b");
			hm.put(3,"c");
		//keySet方式取出
			Set keySet = hm.keySet();
			//因为Map不能直接用Iterator,故还是和Map遍历的方式一样,先要建立一个Set,存Key或者存关系,用for遍历的其实是这个Set
			for(Integer i : keySet)
			{
				System.out.println(i+"::"+hm.get(i));
			}


		//entrySet方式取出
			for(Map.Entry me : hm.entrySet())//简化书写
				//Set> entrySet = hm.entrySet();
				//for(Map.Entry me : entrySet)
			{
				System.out.println(me.getKey()+"------"+me.getValue());
			}
	}
}

方法的可变参数

class  ParamMethodDemo  
{  
	//主函数调用方法
	    public static void main(String[] args)   
	    {  	
	    //改进版老方法调用
	    	int arr[] = {3,4,4,5};//缺点:每次都要新建一个数组
	        showOld2(arr);//输出:3,4,4,5
	        int arr2[] = {2,3,4,5,6};//输出2,3,4,5,6
	        showOld2(arr2);
	     //新版本方法调用   
	        showNew(3,4,4,5);//输出:3,4,4,5
	    	showNew(2,3,4,5,6); //输出:2,3,4,5,6
	    }  
	    
    //老的方法:传入参数不确定时,需要写许多超载的方法
		public static void showOld(int a,int b){
			System.out.println(a + "," + b);
		}
		public static void showOld(int a,int b,int c){
			System.out.println(a + "," + b + "," + c);
		}
	//老方法的改进版:传入一个数组
		public static void showOld2(int[] arr){
			for(int i = 0; i < arr.length; i++){
				if(!(i == arr.length - 1)){
					System.out.print(arr[i] + ",");//用print就可以实现不换行打印
				}else{
					System.out.println(arr[i]);
				}
			}
		}
		
	//新的方法:可变参数的方法
	    public static void showNew(int... arr)//...就表示可变参数
	    //此时不用再手动每次都new一个Array了,只要将要操作的元素作为参数传递即可,隐式将这些参数封装成了数组.
	    //在使用时注意:可变参数一定要定义在参数列表最后面,如public static void show(String str,int... arr)
	    {  
			for(int i = 0; i < arr.length; i++){
				if(!(i == arr.length - 1)){
					System.out.print(arr[i] + ",");//用print就可以实现不换行打印
				}else{
					System.out.println(arr[i]);
				}
			}
	    }  
}  

静态导入

import java.util.*;  
import static java.util.Arrays.*; //导入的是Arrays这个类中的所有静态成员。
import static java.lang.System.*;  
  
class  StaticImport //extends Object  
{  
    public static void main(String[] args)   
    {  
    //打印输出时就可以直接省略书写System. (import static java.lang.System.*)
        out.println("haha");
    
    //使用Arrays工具类的方法sort时就可以省略书写Array.  (import static java.util.Arrays.*)
        int[] arr = {3,1,5};  
        sort(arr);
        int index = binarySearch(arr,1);//二分查找也是一样可以省略  
        out.println("Index="+index);  
  
    //当没有指定继承时,所以类默认继承了Object
       out.println(Arrays.toString(arr)); //但是toString方法所有Object都具备,所以为了区分,必须写上具体调用者  
    }  
}  

集合与数组互转

数组变成集合

asList:将数组变成List集合,具体格式为:Lsit asList(T... a);
使用注意事项:
1,将数组转换成集合,不可使用集合的增删方法,因为数组的长度是固定的。如果进行增删操作,则会发生UnsupportedOperationException的编译异常。
2,如果数组中的元素都是对象,则变成集合时,数组中的元素就直接转为集合中的元素。
3,如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在。
import java.util.*;
class  ArraysDemo  
{  
    public static void main(String[] args)   
    {  
    	//数组初始化
        	int[] intArr = {2,4,5}; 
        	String[] strArr = {"abc","cc","kkkk"};//字符串数组
        	Integer[] INTArr = {2,4,5}; //整数包装类数组
        	
        //转换为字符串形式   
        	System.out.println(Arrays.toString(intArr)); //输出:[2, 4, 5]
        
        //数组转为List
        	List list = Arrays.asList(strArr);  
        	sop("contains:"+list.contains("cc"));//判断是否存在"cc"这个元素,输出:contains:true
        	/*	数组变成list集合的好处:可以使用集合的思想和方法来操作数组中的元素
        	 	将数组变成集合,不可以使用集合的增删方法。因为数组的长度是固定
        	 	如果你增删。那么会发生UnsupportedOperationException
        	 	可以使用的方法:
	        	 	contains
					get
					indexOf()
					subList()
        	 */
        	//如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在
        	List li1 = Arrays.asList(intArr);
        	sop("asList--int[]转集合:" + li1);//输出:asList--int[]转集合:[[I@659e0bfd]
        	
        	//如果数组中的元素都是对象,则变成集合时,数组中的元素就直接转为集合中的元素。
	        List li = Arrays.asList(INTArr);  
	        sop("asList--Integer[]转集合:" + li); //输出:asList--Integer[]转集合:[2, 4, 5]   
    }  
  
		//打印方法  
		    public static void sop(Object obj)  
		    {  
		        System.out.println(obj);  
		    }  
}  

集合变成数组

基本方法:使用Collection接口中的toArray方法。
使用注意事项:
1,指定类型的数组到底要定义多长呢?
当指定类型的数组长度小于集合的size,那么该方法内部会创建一个新的数组。长度为集合的size。
当指定类型的数组长度大于集合的size,就不会创建新数组,而是使用传递进来的数组。
所以创建一个刚刚好的数组最优。
2,为什么要将集合变数组?
为了限定对元素的操作。不需要进行增删了。
import java.util.*;
class  Coll2Arr
{
	public static void main(String[] args) 
	{
	//ArrayList初始化
		ArrayList al = new ArrayList();
		al.add("abc1");
		al.add("abc2");
		al.add("abc3");
	//定义一个String数组,使用toArray方法	
		String[] arr = al.toArray(new String[al.size()]);//传一个指定类型的数组,数组的长度等于集合的长度al.size()
	//打印输出
		System.out.println(Arrays.toString(arr));//输出:[abc1, abc2, abc3]
	}
}




你可能感兴趣的:(JAVA,java,泛型)