黑马程序员--自学笔记--集合(其三)

集合(其三)

-------- android培训java培训、期待与您交流!--------

八.泛型

    在JDK1.5中,使用泛型技术来接收类中要操作的引用数据类型,并用于解决数据的安全问题,是一个安全机制。当类中操作的引用数据类型不确定的时候,就可以使用泛型类。
    泛型的优点:
        ① 将运行时期的问题ClassCastException转到了编译时期;
        ② 避免了强制转换时的麻烦。
    泛型的使用:通过<>来定义要操作的引用数据类型,通常在集合框架中比较常见,只要见到<>就要定义泛型。泛型类的使用:当类中要操作的引用数据类型不确定时,可以定义泛型扩展,早期是通过定义Object来完成扩展。当操作的引用数据类型不确定时,就可以使用<泛型>,将要操作的引用数据类型传入<>中即可。<>其实就是一个用于接收具体引用数据类型的参数范围。
    泛型技术是编译器使用的技术,用于编译时期,是用来确保类型安全的安全机制。运行时,为了兼容运行的类加载器,编译器会将泛型去掉,生成的class文件中不带泛型,这就是所谓的泛型擦除。与此同时,在运行时,还将通过元素的类型自动进行转换动作,不需要使用者再进行强制转换操作了,这就是所谓的泛型补偿机制。
注意:
    ① Object类中的equals(Object obj)方法是由Object定义,所有继承它的类都不可以修改该方法中参数的类型,且在进行强制转换时必须进行健壮性判断。
    ② 泛型<>中不可以写入基本数据类型,只能够写入引用数据类型。(int[]可以写入)
1.自定义泛型类
代码演示:
<span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;">//由于是自定义泛型类,所以<>中的可以写入任意字母,不过要注意要与类中代码相对应
public class GenericityDemo<C> {
	
	private C q;

	public C getObject() {
		return q;
	}

	public void setObject(C object) {
		this.q = object;
	}
}</span></strong></span>
2.自定义泛型方法
代码演示:
<span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;">	//自定义泛型方法时需要在返回值之前进行标识
	public <W> void show(W str){
		System.out.println("show : "+str.toString());
	}</span></strong></span>
   泛型需要对象来明确,但是静态方法不需要对象来调用。当方法为静态时,将不能访问类上定义的泛型。如果要在静态方法上使用泛型,就只能将泛型定义在方法上,例如:(关联上边的自定义泛型类)
<span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;">	public static <Y> void method(Y obj){
		System.out.println(obj) ;
	}</span></strong></span>
注意:
    一旦使用泛型,变量类型不确定,不能使用具体对象的方法。但是能够使用Object类中定义的方法。
3.自定义泛型接口
代码演示:
<span style="font-family:KaiTi_GB2312;"><strong><span style="font-size: 14px;"><span style="font-family:KaiTi_GB2312;">//自定义泛型接口,将泛型定义在接口上。 
interface Inter<T>{
	public void show(T t);
}
//如果实现它的类的数据类型还是不确定,可以继续使用泛型进行定义
class InterImpl2<Q> implements Inter<Q>{
	public void show(Q q){
		System.out.println("show :"+q);
	}
}
//如果实现他的类的数据类型已经确定,则可以直接将已确定的引用数据类型写入泛型中
class InterImpl implements Inter<String>{
	public void show(String str){
		System.out.println("show :"+str);
	}
</span><span style="font-size:14px;">}</span></span></strong></span>
4.泛型上限
    当无法确定泛型的具体类型时,可以使用<?>来表示。
    泛型上限的表现形式:
        <? extends 父类(上限)> ---- 此定义方式只接收父类或其子类的引用数据类型
    一般存储元素是使用泛型的上限,因为这样取出的都是按照上限类型来运算的,就不会出现类型安全隐患。可以参考理解Collection类中的addAll(Collection<? extends E> c)方法,该方法所传入的参数类型即使用了泛型的上限。
5.泛型下限
    泛型下限的表现形式:
        <? super E> ---- 此定义方式只接受E类型或者是E的父类型的引用数据类型   
    通常对集合中的元素进行取出操作时,使用泛型的下限。可以参考理解TreeSet集合中的构造方法:TreeSet(Comparator<? super E> comparator),该方法是构造出一个新的空TreeSet集合,并根据指定的比较其进行比较。
注意:    
    涉及到equals(Object obj)/containsAll()/removeAll()方法时,由于都引用到了Object类中定义的equals(Object obj)方法。因为不可改变Object类中定义的equals(Object obj)方法的参数类型,所以当类型不确定时,可以在参数传递定义<?>来进行类型表示。

九.集合框架工具类

1.Collections
    Collections是集合框架中的工具类,该类中的方法都是静态的。
(1)sort(List<T> list)
原理演示:
<span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;">	//所传入的数据类型需要使用到自然排序,所以其泛型要继承Comparable
	public static <T extends Comparable<? super T>> void mySort(List<T> list) {

		//使用选择排序法实现sort()方法的原理
		for (int i=0;i<list.size()-1;i++) {
			for (int j=i+1;j<list.size();j++) {
				//调用compareTo()方法(返回正数负数或者是零)比较两个值的大小,如果是大于调换两个值的位置
				if (list.get(i).compareTo(list.get(j))>0) {
					T temp = list.get(i);
					list.set(i, list.get(j));
					list.set(j, temp);
				}
			}
		}
	}</span></strong></span>
如果不想依赖自然排序,可以通过传入比较器来实现自定义排序:
<span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;">	//如果不使用到自然排序,可以传入一个比较器来实现自定义排序
	public static <T> void mySort(List<T> list,Comparator<? super T> comp) {

		//使用选择排序法实现sort()方法的原理
		for (int i=0;i<list.size()-1;i++) {
			for (int j=i+1;j<list.size();j++) {
				//调用比较器中的compareo()方法(返回正数负数或者是零)比较两个值的大小,如果是大于调换两个值的位置
				if (comp.compare(list.get(i), list.get(j))>0) {
					T temp = list.get(i);
					list.set(i, list.get(j));
					list.set(j, temp);
				}
			}
		}
	}</span></strong></span>
(2)swap(List i,List j)
    以上代码中将i,j两个元素的位置进行交换使用的代码是:
        T temp = list.get(i) ;    // 定义一个临时变量用于暂存数据
        list.set(i,list.get(j)) ;
        list.set(j,temp) ;
    引入集合框架工具类之后,可以简化为:Collections.swap(i,j) ;
(3)折半查找和获取最值
    进行折半查找集合必须是有序的(从大到小或者是从小到大),对于无序的集合可以先进行排序之后在进行折半查找。如果找到指定的内容,将会返回指定内容的位置;如果找不到指定内容,则返回该指定内容在集合中的插入点-1.
代码演示:
    Collections.binarySearch(list,"所要查找的内容") ;
    如果要获取集合中的最大值,可以使用Collections的max()方法来获取。但在使用之前应该先判断所指定的集合是否能够排序,如果可以根据其自然顺序进行排序,则可以直接使用。但是如果指定集合不可以根据自然顺序排序,则在调用max()方法时要将比较其作为参数传入。
代码演示:
    Collections.max(list,comparator) ;  
(4)集合逆序和元素替换
① reverseOrder()
代码演示:
    TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder()) ;
    // reverseOrder()方法返回一个比较器,并强行逆转元素原有的自然排序顺序。
② reverseOrder("比较器对象")
代码演示:
    TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder(new ComparatorByLength()));
    // reverseOrder("比较器对象")方法将一个已有的比较器进行顺序逆转。(需要传入一个比较器对象)
③ reverse(List<?> list)
    该方法将指定列表集合的元素顺序进行反传
④ replaceAll(List<?> list,old,new)
    该方法先调用indexOf(old)找到集合中old值的位置index,然后调用set(index,new)将新值覆盖旧值。(即:set(indexOf(old),new) ;)
⑤ fill(list,"值")
    该方法将全部元素转变为所传入的值。
⑥ shuffle(list)
    该方法将集合中的所有元素随机安放于任意位置
(5)toArray()
    该方法可以将集合转换成数组。作此转换的目的是为了限定操作。(转换之后不允许增删)
关键代码:
    (返回值类型:T[]) toArray(T[] a) ;    // toArray()方法需要传入一个指定类型的数组
范例:
    String[] s = list.toArray(new String[list.size()]) ;    //使用list.size()方法指定数组大小
    注意在长度定义时,如果长度小于集合的size,那么自动创建同类型同size的数组;如果所定义的长度大于集合的size,那么该方法使用指定的数组存储集合元素,其他多余位置默认为null。
2.同步非同步的集合
    由于常用集合线程都是非同步的,所以再多线程时,要给非同步的集合加锁使其线程同步。
范例代码演示: (该范例为常见加锁方法,可以在以后开发中使用。注意:注释包含知识点和注意点)
<span style="font-family:KaiTi_GB2312;"><strong><span style="font-family:KaiTi_GB2312;">class MyCollections {

	public static List synList(List list){
		return new MtList(list) ;
	}
	
	//通过内部类实现为集合加锁
	private class MyList implements List{
		
		private List list ;
		//定义一个Object对象的锁
		private static final Object lock = new Object() ;
		MyList(List list){
			this.list = list ;
		}
		//将List对象原有的add()方法进行同步包装
		public boolean add(Object obj){
			synchronized(lock){
				return list.add(obj) ; 
			}
		}
		//将List对象原有的remove()方法进行同步包装
		public boolean remove(Object obj){
			synchronized(lock){
				return list.remove(obj) ;
			}
		}
		//继续实现List接口中的抽象方法
		//…………	
		//…………
		//…………
		//…………
	}
}</span></strong></span>
    以上代码是一下代码的实现原理:
        (返回值类型:List) synchronizedList(List<?> list) ;
        //此代码的实现过程即为以上代码,通过此构造方法,可以返回指定集合列表支持的同步集合列表(线程安全)。其中synchronizedList还可以换成是synchronizedMap和synchronizedSet……
3.Arrays工具类
    与Collections一样,Arrays也是是集合框架中的工具类,该类中的方法也都是静态的。
(1)(返回值类型:List) asList(T...a[])
    该方法将数组转换成集合。使用此方法是因为数组能使用的方法有限,利用此方法可以用使用集合中的方法操作数组中的元素。但是要注意,由于数组的长度固定,转换成集合之后不可以使用增删方法。
    如果数组中的元素是对象,那么在转成集合的时候,直接将数组中的元素作为集合中的元素进行集合存储;但是如果数组中的元素是基本数据类型数值,那么会将该数组作为集合中的元素进行存储(即存储数组的地址)。
(2)sort()
    该方法可以对数组中的元素进行排序
(3)……(详细参见API文档)
4.JDK1.5新特性
(1)foreach语句
代码演示:
    for(类型 变量 : Collection集合/数组){}
范例:(使用高级for遍历Map集合)
    可将Map转成单列Set集合,再利用高级for进行遍历
        for(Integer key : map.keySet()){
            String value = map.get(key) ;
            System.out.println(……) ;
        }
    或者是:
        for(Map.Entry<Integer,String> me : map.entrySet()){
            Integer key = me.getKey() ;
            String value = me.getValue() ;
            System.out.println(……) ;
        }
传统for循环与高级for循环的区别:
    ① 传统for循环可以完成对语句的多次执行,并且可以控制循环的条件以及循环的次数;
    ② 高级for循环必须有被便利的目标,该目标要么是数组要么是Collection单列集合;
    ③ 如果需要对数组的角标进行操作,需要使用传统for循环。
(2)函数可变参数
    关键代码演示:
        public static int newAdd(int...arr){}    // 其实可变参数是数组的简写
    函数可变参数其实是一个数组,但是接受的是数组的元素。它自动将这些元素封装成数组,极大地简化了使用者的书写。但是要注意的是:可变参数的类型必须定义在参数列表的结尾。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值