黑马程序员________Java集合Map及其JDK1.5新特性泛型机制的学习笔记

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


        Map接口的子类特点是将对象成键值对的方式,成对存入集合,并且要保证键的唯一性。Map接口子类方法所共有的方法定义在Map中。

public V put(K key,V value)-- >将元素key和元素value成键值对存入集合并返回原值,如果原来没有则返回null


public void putAll(Map<?extendsK,?extends V > m) -- >将Map集合m的映射关系复制到该集合中,要求m集合的键值类型参数必须是原集合键值的类型参数的子类


public void clear()-- >将Map集合中的映射关系全部清除

 

public V remove(K key) -- >清除Map中键key与其对应值的映射关系,并返回该值,如果没有则返回null

 

public boolean containsValue(V value) -->判断集合中是否存在该值

 

public Boolean containsKey(K key) -- >判断集合中是否存在该键

 

public Boolean isEmpty()-- >判断集合是否为空

 

public V get(K key) -- >返回集合中key所对应的键值

 

public int size()-- >获取集合中键值对的个数

 

public Collection<V> values()-- >得到Map集合中所有的value值以Collection的方式返回

 

public Set<K> keyset()-- > 得到Map集合中所有键的值,因为键的唯一性,所以是以Set的方式

 

public Set<Map.Entry<K,V>>entrySet()-- >得到Map集合中所有键值对映射关系的Set集合

 

         Map中取出元素的方法有两种,一种是用keySet方法得到key的元素集合,然后迭代该集合,并用取到的key值来获得value值。假设一个Map集合的参数化类型为 <Student,Teacher>。

Set<Student> hs=map.keySet();//将键存入hs集合

Iterator it=hs.iterator();//获取hs集合迭代器

while(it.hasNext())

{

key=it.next();   //得到某个key键

System.out.println(key+”:”+map.get(key));//打印key键和通过key键得到的value值

}


        另外一种方法是通过entrySet方法,获得一个该Map集合中键值对关系的Set集合。然后迭代这个集合,通过每一个元素Map.Entry的方法来分别获取键和值。

Set<Map.Entry<Student,Teacher>>hs=hm.entrySet();

Iterator<Map.Entry<Student,Teacher>>it=hs.iterator();//该Set集合中的元素类型是Map.Entry

while(it.hasNext())

{

Map.Entry<Student,Teacher>me=it.next();//获取到Map.Entry元素

System.out.println(me.getKey()+”:”+me.getValue());

}

 

         Map接口下的子类有HashTable,HashMap,TreeMap。

         HashMap底层是通过哈希表来保证键的唯一性的,如果我们想要自定义一类元素是通过那些属性来判断是否是同一元素,可以覆写类中的hashCode方法和eqals方法,同HashSet,但同时也要谨慎修改对象参与hashCode计算的属性,同HashSet一样,如果修改该属性,会导致删除该对象的时候找不到该对象在此集合中的哈希值而删除失败,从而造成内存泄露。线程不同步。其中一个子类是LinkedHashMap,他底层除了哈希表还实现了链表结构,所以能够按照存入的顺序先进先出的取出元素。

        

         HashTable底层也是哈希表,同HashMap相同,不同的是HashTable是线程同步的。在特定条件下这提高了安全性,但是在大多数情况下降低了效率。HashTable有一个子类比较常用就是Properties,他在我们日后的开发中经常和IO技术配齐起来操作配置文件。

 

         TreeMap可以讲存入的键值对按照键的自然顺序进行排序。如果我们想要干预这个排序的规则,我们只需要让键的类实现Comparable接口,然后再类中覆写compareTo方法即可,同TreeSet。这个集合是线程不同步的。以下是一个小练习,要求将老师和学生想对应的存入集合,并按学生年龄进行排序。

import java.util.*;

class TreeMapDemo

{

         publicstatic void main(String[] args)

         {

                   TreeMap<Student,Teacher>hm=new TreeMap<Student,Teacher>();

                   hm.put(newStudent("student01",11),new Teacher("teacher01",21));

                   hm.put(newStudent("student02",12),new Teacher("teacher02",22));

                   hm.put(newStudent("student03",13),new Teacher("teacher03",23));

                   Set<Map.Entry<Student,Teacher>>hs=hm.entrySet();

                   Iterator<Map.Entry<Student,Teacher>>  it=hs.iterator();

                   while(it.hasNext())

                   {

                            Map.Entry<Student,Teacher>me=it.next();

                            Students s=me.getKey();

                            Teachert t=me.getValue();

                            s.show();

                            t.showt();

                   }

         }

}

 

class Student implementsComparable<Student>

{

        

         privateString name;

         privateint age;

         Student(Stringname,int age)

         {

                   this.name=name;

                   this.age=age;

         }

         publicvoid show()

         {

                   System.out.println(+name+"---"+age);

         }

        

        

         publicint compareTo(Student s)//覆写这个方法是为了让Student按照我们的意志去比较

         {

                   intnum=new Integer(this.age).compareTo(new Integer(s.age));

                   if(num==0)

                            returnthis.name.compareTo(s.name);

                   returnnum;

         }

}

 

class Teacher

{

         privateString name;

         privateint age;

         Teacher(Stringname,int age)

         {

                   this.name=name;

                   this.age=age;

         }

         voidshow()

         {

                   System.out.println(name+"---"+age);

         }

}

 

 


         泛型是java在jdk1.5之后才出现的。因为操作集合的时候每次都要判断元素类型,会出现ClassCastException。所以引入了泛型机制,使元素类型在编译的时候就做了限定。例如定义一个Collection <String> c=new Collection<String> ();那么这个Collection集合中就只能存入String类型的对象。


        参数化类型不考虑类型参数的继承关系,比如Vector<String>v = new Vector<Object>()或者Vector<Object> v = new Vector<String>();都是错误的。如果想达到这个效果可以用通配符来做到。如Vector<?extends Object> v=new Vector<String>()。


        使用?通配符可以引用其他各种参数化类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。如我们定义一个打印参数化集合的方法。

public voidprintCollection(Collection<?> collection )

{

         for(Objectobj:collection)

                   System.out.println(obj);

}

        

collection.size();//正确,因为此方法与类型参数没有关系

collection.add(“abc”);//错误,因为collection不能确定自己匹配的参数是String

         通配符的上限是?extends T;通配符的下限是? super T。

 

 

 

泛型是提供给Javac编译器使用的。可以限定集合中输入的类型,让编译器挡住原始程序的非法输入,编译器编译带类型说明的集合时会去掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样,由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。

 

        对于一个指定泛型为Integer的集合Collection<Integer>c1=new ArrayList<Integer>();可以通过Collection.class.getMethod(“add”,Object.class).invoke(c1,”abc”);给c1添加字符串对象。


         需要注意的几点是泛型的实际参数只能是引用类型不可以是基本类型,泛型定义在类上时。

类中的静态成员不具备该类型参数

        

        泛型方法在编译时参数类型的类型推断,规则如下:

 

        1、当这个类型变量只在整个参数列表中或者返回值中的被用到一次,那么直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如方法static <E> void swap(E[] a, int i, int j),调用swap(new String[3],3,4)即可确定E是String类型。

        2、当某个类型变量在整个参数列表中或者返回值中的多处被用到,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,例如:static <T> T add(T a, T b)  调用add(3,5)很容易就可以确定T的类型是Integer。

        3、当某个类型变量在整个参数列表中的或者返回值中的多处被用到了,而调用方法时这多处的实际应用类型对应到了不同的类型且没有使用返回值这时候取多个参数中的最大交集类型,例如static <T> void fill(T[] a, T v),在调用fill(newInteger[3],3.5f) 的时候可以确定T的类型是Number,编译可以通过但是运行的时候可能会出问题。

        4、当某个类型变量在整个参数列表中或者返回值中的多处被用到,而调用方法时这多处的实际应用类型对应到了不同的类型并且使用返回值,这时候类参数类型为返回值的类型,例如static <T> T add(T a, T b),调用int x =add(3,3.5f) 即T的参数类型为Integer。因为参数列表中的第二个参数3.5f是float类型,不满足Integer。而我们如果把调用函数时的代码改为Number x=add(3,3.5f)则通过编译。

        5、参数类型的类型推断具有传递性。例如一个集合Collection<String> c=new Collection<String>();一个参数化的方法public<T>void method(Collection<T> c,T[] t);这个时候调用方法method(c,newint[]);编译器会报错,他将集合c中的参数类型T传递给了方法。而与方法中第二个参数的实际参数不符。

 

         如果我们想让自定义的参数类型作用于整个类就需要将泛型定义在类名后面。在引用该类名创建对象的时候,根据指定的类型信息来参数化类型变量。例如我们自己定义一个类。

class Tool<E>{

public void add(E obj){}

}

这个时候我们创建对象的时候Tool<Person> t=new Tool<Person>();就可以限定该对象t的add方法只能操作Person对象。

         如果一个类中有个别方法用到的类型变量和类上的类型变量不同,只需要在类中的方法再次定义一个类型变量即可。还是刚才的例子

class Tool<E>{

public void add(E obj){}

public<T> method(T obj)

}

这样再创建Tool对象,参数化该类的类型变量为Person,而调用method方法时可以传入Animal对象进行操作而不受限于类上定义的类型参数。

 

         java中给我们提供了一个工具类Collections来方便我们操作集合,在该类中的全部方法均为static静态方法,可以直接通过类名调用。其中比较常用的方法有以下方法:

void sort(List list,Comparator com)-- >如果我想将一个List数组进行排序,需要将数组中元素取出再放入一个TreeSet集合然后再传入一个比较器,而用这个方法直接传入一个比较器就可以了

 

void max(List list,Comparator com)-- >根据比较器取出最大值

 

int binarySearch(List list,Object key)-- >找出List集合元素key并返回搜索键的索引。注意执行该方法时必须要对集合进行sort操作,否则结果是不明确的,或者在参数中再传入一个比较器。如果没有该元素,则返回(-(插入点)-1)。

 

void fill(List list,Object obj)-- >将list集合中所有元素换为obj

 

void replaceAll(List list,ObjectoldValue,Object newValue)-- >将集合中所有为oldValued的元素换成newValue元素。

 

void reverse(List list)-- >将集合翻转

 

Comparator reverseOrder()-- >返回一个比较器,可以讲集合内元素顺序倒转。

 

CollectionsynchronizedCollection(Collection collection) -- >通过传入集合获得一个同步的集合

List synchronizedList(List list)

Set synchronizedSet(Set set)

Map synchronizedMap(Map map)

 

void swap(List list,int index1,int index2)-->将集合list中角标为index1和index2的元素互换位置

 

void shuffle(Listlist)-- >将集合内部元素随机交换,用于打乱顺序,可在参数中添加一个Randam对象来指定随机源

 

 

 

        此外java还为我们提供了一些其他的工具类,常用的如Arrays、System、Runtime、Date、Calendar、Math等工具类。要记住他们所有的方法几乎是不现实的,所以我们增强自己API文档的阅读能力是必须的。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值