黑马程序员——java基础知识之泛型、集合(Map、工具类等)

来源:互联网 发布:js设置button颜色 编辑:程序博客网 时间:2024/06/05 03:59

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
(一)、泛型
1、泛型:
JDK1.5版本以后出现新特性。用于解决安全问题,是一个类型安全机制。

2、泛型有什么好处?
①将运行时期出现问题ClassCastException,转移到了编译时期。,
方便于程序员解决问题。让运行时问题减少,安全。,
②避免了强制转换麻烦。

3、泛型的格式
泛型格式:通过<>来定义要操作的引用数据类型。

4、在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见,
只要见到<>就要定义泛型。
其实<> 就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。

例子程序:

class GenericDemo {    public static void main(String[] args)     {        //给集合加泛型        ArrayList<String> al = new ArrayList<String>();        al.add("abc01");        al.add("abc0991");        al.add("abc014");        //al.add(4);//al.add(new Integer(4));        //注意迭代器也要加泛型        Iterator<String> it = al.iterator();        while(it.hasNext())        {            String s = it.next();            System.out.println(s+":"+s.length());        }    }}

例子程序:
注意比较器的时候使用泛型:

class GenericDemo2 {    public static void main(String[] args)     {        TreeSet<String> ts = new TreeSet<String>(new LenComparator());        ts.add("abcd");        ts.add("cc");        ts.add("cba");        ts.add("aaa");        ts.add("z");        ts.add("hahaha");        Iterator<String> it = ts.iterator();        while(it.hasNext())        {            String s = it.next();            System.out.println(s);        }    }}//给比较器加泛型class LenComparator implements Comparator<String>{    public int compare(String o1,String o2)    {        int num = new Integer(o2.length()).compareTo(new Integer(o1.length()));        if(num==0)            return o2.compareTo(o1);        return num;    }}

5、泛型定义在类上
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,
早期定义Object来完成扩展。
现在定义泛型来完成扩展。

例子程序:

class Worker{}//泛型前做法。class Tool{    private Object obj;    public void setObject(Object obj)    {        this.obj = obj;    }    public Object getObject()    {        return obj;    }}//泛型后的做法class Utils<QQ>{    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<Worker> u = new Utils<Worker>();        u.setObject(new Student());        Worker w = u.getObject();;        //加泛型前        /*        Tool t = new Tool();        t.setObject(new Student());        Worker w = (Worker)t.getObject();        */    }}

5、泛型定义在方法上
①泛型类定义的泛型,在整个类中有效。如果被方法使用,
那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
②为了让不同方法可以操作不同类型,而且类型还不确定。
那么可以将泛型定义在方法上。
例子程序:

class Demo{    //把泛型定义在方法上    public <T> void show(T t)    {        System.out.println("show:"+t);    }    public <Q> void print(Q q)    {        System.out.println("print:"+q);    }    public  static <W> void method(W t)    {        System.out.println("method:"+t);    }}class GenericDemo4 {    public static void main(String[] args)     {        Demo d = new Demo();        //调方法时你传什么类型,方法使用什么类型        d.show("haha");        //d.show(4);        d.print(5);        d.print("hehe");        d.show(new Integer(4));        d.print("hah");    }}

注意:
①泛型定义在类中和定义在方法上,可以一起使用
②静态方法不可以访问类上定义的泛型。静态方法上使用泛型,要把泛型定义在方法上。它的格式是:public static void method(W t)

6、泛型定义在接口中

//接口定义泛型interface Inter<T>{    void show(T t);};//实现类不知道什么类型,也使用泛型,实现Inter接口class InterImpl<T> implements Inter<T>{    public void show(T t)    {        System.out.println("show :"+t);    }}class GenericDemo5 {    public static void main(String[] args)     {        InterImpl<Integer> i = new InterImpl<Integer>();        i.show(4);    }}

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

例子程序:

//程序的一部分,利用泛型可以接受Person类和Person的子类,也就是上限public static void printColl(Collection<? extends Person> al)    {        Iterator<? extends Person> it = al.iterator();        while(it.hasNext())        {            System.out.println(it.next().getName());        }    }

总结:
对泛型的定义:
第一、定义泛型:当又不确定的类型需要传入到集合中,需要定义泛型。
第二、定义泛型类:如果类型确定后,所操作的方法都是属于此类型,则定义泛型类。
第三、定义泛型方法:如果定义的方法确定了,里面所操作的类型不确定,则定义泛型方法。

(二)、集合框架(容器)之Map
Map集合:该集合存储键值对。一对一对往里存。而且要保证键的唯一性。

Map的主要方法:
1,添加。
put(K key, V value)
putAll()
2,删除。
clear()
remove(Object key)
3,判断。
containsValue(Object value)
containsKey(Object key)
isEmpty()

4,获取。
get(Object key)
size()
values()
entrySet()
keySet()

Map的主要子类:

Map
|–Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。jdk1.0.效率低。
|–HashMap:底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将hashtable替代,jdk1.2.效率高。
|–TreeMap:底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。

Map方法例子程序:

import java.util.*;class  MapDemo{    public static void main(String[] args)     {        Map<String,String> map = new HashMap<String,String>();        //添加元素,添加元素,如果出现添加时,相同的键。那么后添加的值会覆盖原有键对应值。        //并put方法会返回被覆盖的值。        System.out.println("put:"+map.put("01","zhangsan1"));        System.out.println("put:"+map.put("01","wnagwu"));        map.put("02","zhangsan2");        map.put("03","zhangsan3");        System.out.println("containsKey:"+map.containsKey("022"));        //System.out.println("remove:"+map.remove("02"));        System.out.println("get:"+map.get("023"));        map.put("04",null);        System.out.println("get:"+map.get("04"));        //可以通过get方法的返回值来判断一个键是否存在。通过返回null来判断。        //获取map集合中所有的值。        Collection<String> coll = map.values();        System.out.println(coll);        System.out.println(map);    }}

map集合的两种取出方式:
①Set keySet:将map中所有的键存入到Set集合。因为set具备迭代器。
所有可以迭代方式取出所有的键,在根据get方法。获取每一个键对应的值。
Map集合的取出原理:将map集合转成set集合。在通过迭代器取出。

②Set<> entrySet:将map集合中的映射关系存入到了set集合中,
而这个关系的数据类型就是:Map.Entry
Entry其实就是Map中的一个static内部接口。

注意:为什么要定义在内部呢?
因为只有有了Map集合,有了键值对,才会有键值的映射关系。
关系属于Map集合中的一个内部事物。而且该事物在直接访问Map集合中的元素。

例子程序:

import java.util.*;class MapDemo2 {    public static void main(String[] args)     {        Map<String,String> map = new HashMap<String,String>();        map.put("02","zhangsan2");        map.put("03","zhangsan3");        map.put("01","zhangsan1");        map.put("04","zhangsan4");//利用第二种方式,entrySet();//将Map集合中的映射关系取出。存入到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();            System.out.println(key+":"+value);        }        /*//第一种方法:利用keySet();        //先获取map集合的所有键的Set集合,keySet();        Set<String> keySet = map.keySet();        //有了Set集合。就可以获取其迭代器。        Iterator<String> it = keySet.iterator();        while(it.hasNext())        {            String key = it.next();            //有了键可以通过map集合的get方法获取其对应的值。            String value  = map.get(key);            System.out.println("key:"+key+",value:"+value);        }        */    }}

Map的练习题:训练两种获取map集合元素的方法

package cn.dhjJH;import java.util.*;/*每一个学生都有对应的归属地。学生Student,地址String。学生属性:姓名,年龄。注意:姓名和年龄相同的视为同一个学生。保证学生的唯一性。步骤:1,描述学生。2,定义map容器。将学生作为键,地址作为值。存入。3,获取map集合中的元素。*/public class Test05 {    public static void main (String[] args){        HashMap<Student,String> hm = new HashMap<Student,String>();        hm.put(new Student("zhang1",22), "shanghai");        hm.put(new Student("zhang2",25), "wuhan");        hm.put(new Student("zhang3",26), "changsha");        hm.put(new Student("zhang4",21), "zhuzhou");        //第一种获取map集合中元素的方法,通过keySet方法        Set<Student> keySet = hm.keySet();        Iterator<Student> it = keySet.iterator();        while(it.hasNext()){            Student stu = it.next();            String adr = hm.get(stu);            System.out.println(stu.getName()+"..."+stu.getAge()+"..."+adr);        }        //第二种获取map集合元素的方法,通过entrySet方法        Set<Map.Entry<Student,String>> entrySet = hm.entrySet();        Iterator<Map.Entry<Student,String>> its = entrySet.iterator();        while(its.hasNext()){        //用Map.Entry键值关系来接收,迭代器后的元素            Map.Entry<Student, String> me = its.next();            Student st = me.getKey();            String adrrs = me.getValue();            System.out.println(st.getName()+"..."+st.getAge()+"..."+adrrs);        }    }}class Student implements Comparable<Student>{    private String name;    private int age;    public Student(String name, int age) {        super();        this.name = name;        this.age = age;    }    //获取name和age的方法    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    //重写了排序的方法,提供treeSet的集合排序方法,使Student类具有比较性    public int compareTo(Student s){        int sum = new Integer(this.age).compareTo(new Integer(s.age));        if(sum==0)            return this.getName().compareTo(s.getName());        return sum;    }    //重写hashCode和equals方法,当使用哈希表的数据结构式,保证元素不会重复。    public int hashCode(){        return name.hashCode()+age*39;    }    public String toString(){        return name+":"+age;    }    public boolean equals(Object obj){        if(!(obj instanceof Student))            throw new ClassCastException("类型不匹配");        Student s = (Student)obj;        return this.name.equals(s.name)&&this.age==s.age;    }}

上一个例子改变:通过treeMap排序:

//Student的外部添加一个比较器class StuNameComparator implements Comparator<Student>{    public int compare(Student s1,Student s2)    {        int num = s1.getName().compareTo(s2.getName());        if(num==0)            return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));        return num;    }}class  MapTest2{    public static void main(String[] args)     {    //通过TreeMap进行元素的排序        TreeMap<Student,String> tm = new TreeMap<Student,String>(new StuNameComparator());        tm.put(new Student("blisi3",23),"nanjing");        tm.put(new Student("lisi1",21),"beijing");        tm.put(new Student("alisi4",24),"wuhan");        tm.put(new Student("lisi1",21),"tianjin");        tm.put(new Student("lisi2",22),"shanghai");        //使用Map类的entrySet方法获取Map容器中的元素        Set<Map.Entry<Student,String>> entrySet = tm.entrySet();        Iterator<Map.Entry<Student,String>> it = entrySet.iterator();        while(it.hasNext())        {            Map.Entry<Student,String> me = it.next();            Student stu = me.getKey();            String addr = me.getValue();            System.out.println(stu+":::"+addr);        }    }}

练习3:

/*练习:"sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数。希望打印结果:a(1)c(2).....通过结果发现,每一个字母都有对应的次数。说明字母和次数之间都有映射关系。注意了,当发现有映射关系时,可以选择map集合。因为map集合中存放就是映射关系。什么使用map集合呢?当数据之间存在这映射关系时,就要先想map集合。思路:1,将字符串转换成字符数组。因为要对每一个字母进行操作。2,定义一个map集合,因为打印结果的字母有顺序,所以使用treemap集合。3,遍历字符数组。    将每一个字母作为键去查map集合。    如果返回null,将该字母和1存入到map集合中。    如果返回不是null,说明该字母在map集合已经存在并有对应次数。    那么就获取该次数并进行自增。,然后将该字母和自增后的次数存入到map集合中。覆盖调用原理键所对应的值。4,将map集合中的数据变成指定的字符串形式返回。*/import java.util.*;class  MapTest3{    public static void main(String[] args)     {        String s= charCount("ak+abAf1c,dCkaAbc-defa");        System.out.println(s);    }    //定义方法    public static String charCount(String str)    {    //把字符串转为数组        char[] chs = str.toCharArray();//用TreeMap容器来转键值对数据TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();        int count = 0;        //遍历字符数组        for(int x=0; x<chs.length; x++)        {            if(!(chs[x]>='a' && chs[x]<='z' || chs[x]>='A' && chs[x]<='Z'))                continue;            Integer value = tm.get(chs[x]);            if(value!=null)                count = value;            count++;            tm.put(chs[x],count);//直接往集合中存储字符和数字,为什么可以,因为自动装箱。            count = 0;            /*            if(value==null)            {                tm.put(chs[x],1);            }            else            {                value = value + 1;                tm.put(chs[x],value);            }            */        }        //System.out.println(tm);//用缓冲的字符串容器去装元素        StringBuilder sb = new StringBuilder();//通过entrySet方法,获取TreeMap里面的每一个元素        Set<Map.Entry<Character,Integer>> entrySet = tm.entrySet();        Iterator<Map.Entry<Character,Integer>>  it = entrySet.iterator();        while(it.hasNext())        {            Map.Entry<Character,Integer> me = it.next();            Character ch = me.getKey();            Integer value = me.getValue();            sb.append(ch+"("+value+")");        }        return sb.toString();    }}

Map的扩展应用:
在很多项目中,应用比较多的是一对多的映射关系,这就可以通过嵌套的形式将多个映射定义到一个大的集合中,并将大的集合分级处理,形成一个体系。

/*map扩展知识。map集合被使用是因为具备映射关系。"yureban"   Student("01" "zhangsan");"yureban" Student("02" "lisi");"jiuyeban" "01" "wangwu";"jiuyeban" "02" "zhaoliu";一个学校有多个教室。每一个教室都有名称。*/import java.util.*;class Student{    private String id;    private String name;    Student(String id,String name)    {        this.id = id;        this.name = name;    }    public String toString()    {        return id+":::"+name;    }}class  MapDemo3{//第一种方法:是通过czbk的hashMap集合,嵌套List集合。    public static void demo()    {        HashMap<String,List<Student>> czbk = new HashMap<String,List<Student>>();        List<Student> reyu = new ArrayList<Student>();        List<Student> jiuye = new ArrayList<Student>();        czbk.put("yureban",reyu);        czbk.put("jiuyeban",jiuye);        reyu.add(new Student("01","zhagnsa"));        reyu.add(new Student("04","wangwu"));        jiuye.add(new Student("01","zhouqi"));        jiuye.add(new Student("02","zhaoli"));        Iterator<String> it = czbk.keySet().iterator();        while(it.hasNext())        {            String roomName = it.next();            List<Student> room = czbk.get(roomName);            System.out.println(roomName);            getInfos(room);        }    }    public static void getInfos(List<Student> list)    {        Iterator<Student> it = list.iterator();        while(it.hasNext())        {            Student s = it.next();            System.out.println(s);        }    }    public static void main(String[] args)     {         demo();        /*        //第二种方法是HashMap集合中嵌套HashMap集合。        HashMap<String,List<Student>> czbk = new HashMap<String,List<Student>>();        HashMap<String,String> yure = new HashMap<String,String>();        HashMap<String,String> jiuye = new HashMap<String,String>();        czbk.put("yureban",yure);        czbk.put("jiuyeban",jiuye);        yure.put("01","zhagnsan");        yure.put("02","lisi");        jiuye.put("01","zhaoliu");        jiuye.put("02","wangwu");        //遍历czbk集合。获取所有的教室。        Iterator<String> it = czbk.keySet().iterator();        while(it.hasNext())        {            String roomName = it.next();            HashMap<String,String> room = czbk.get(roomName);            System.out.println(roomName);            getStudentInfo(room);        }//      getStudentInfo(jiuye);//      getStudentInfo(yure);*/    }    public static void getStudentInfo(HashMap<String,String> roomMap)    {        Iterator<String> it = roomMap.keySet().iterator();        while(it.hasNext())        {            String id = it.next();            String name = roomMap.get(id);            System.out.println(id+":"+name);        }    }}

(三)、collections工具类
1、collections有什么作用?
它的出现给集合操作提供了更多的功能。这个类不需要创建对象,内部提供的都是静态方法。 在Collections工具类中大部分方法是用于对List集合进行操作的,如比较,二分查找,随机排序等。

2、collections的方法
1、查找
Tmax(Collection coll);//根据集合的自然顺序,获取coll集合中的最大元素
Tmax(Collection coll,Comparator< T> comp);//根据指定比较器comp的顺序,获取coll集合中的最大元素
intbinarySearch(Lsit< Comparable< T>> list,T key);//二分法搜索list集合中的指定对象
2、替换
voidfill( list,obj);//将list集合中的全部元素替换成指定对象obj
booleanreplaceAll(List lsit,T oldVal,T newVal);//用newVal替换集合中的oldVal值
void swap(Listlist,int i,int j);/在指定列表的指定位置处交换元素
3排序:
void shuffle(List list);//使用默认随机源对list集合中的元素进行随机排序
void sort(Lsit list);//根据自然顺序对list集合中的元素进行排序
voidsort(List lsit,Comparator< T> c);//根据指定比较器c的排序方式对list集合进行排序
4、反转
reverse(List list);//反转list集合中元素的顺序
Comparator reverseOrder();//返回一个比较器,强行逆转了实现Comparable接口的对象的自然顺序
ComparatorreverseOrder(Comparator cmp);//返回一个比较器,强行逆转了指定比较器的顺序
5、同步的集合
ListsynchronizedList(List list);//返回支持的同步(线程安全的)List集合

(四)用于操作数组的工具类Arrays
1、Arrays的特点
Arrays是用于操作数组的工具类。里边的方法也全是静态的。不需要创建对象。

2、Arrays类中的主要方法:
1、Lsit asList(T… a);//将数组转换为集合
注意:
a、将数组转换成集合,不可使用集合的增删方法,因为数组的长度是固定的。如果进行增删操作,则会产生UnsupportedOperationException的编译异常。
b、如果数组中的元素都是对象,则变成集合时,数组中的元素就直接转为集合中的元素。
c、如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在。
2、binarySearch():二分查找方法,fill():替换方法,sort():排序方法等
特点:可对数组元素进行相应操作,可以接受除boolean之外的其他各种基本数据类型及有序的引用类型数组的参数,且还可以对指定元素的范围,并可根据指定比较器进行相应的操作。
如:sort(T[]a,Comparator< T> c)
fill(int[]a,int from,int to)等
3、String toString();//可以接收各种数组类型参数,并返回指定数组内容的字符串表现形式。
4、sort()排序方法:Arrays.sort();

例子小程序片段:

String[] arr = {"abc","cc","kkkk"};List<String> list = Arrays.asList(arr);sop("contains:"+list.contains("cc"));

(五)集合变数组
Collection接口中的toArray方法
例子程序:

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

(六)增强For循环
它的格式是:
for(类型 变量名 : 要遍历的集合或数组){
}

注意:
对集合进行遍历。
只能获取集合元素。但是不能对集合进行操作。

迭代器除了遍历,还可以进行remove集合中元素的动作。
如果是用ListIterator,还可以在遍历过程中对集合进行增删改查的动作。

传统for和高级for有什么区别呢?
高级for有一个局限性。必须有被遍历的目标。
建议在遍历数组的时候,还是希望是用传统for。因为传统for可以定义脚标。

例子程序:

ArrayList<String> al = new ArrayList<String>();        al.add("abc1");        al.add("abc2");        al.add("abc3");for(String s : al)        {            System.out.println(s);        }

(六)、方法的可变参数
①如果一个方法在参数列表中传入多个参数,个数不确定,那么每次都要复写该方法。这时可以用数组作为形式参数。但是在传入时,每次都需要定义一个数组对象,作为实际参数。在JDK1.5版本后,就提供了一个新特性:可变参数。
②可变参数其实就是数组参数的简写形式。不用每一次都手动的建立数组对象。只要将要操作的元素作为参数传递即可。隐式将这些参数封装成了数组。
③在使用时注意:可变参数一定要定义在参数列表的最后面。
例子程序:

   public static void main(String[] args)       {          show("haha",2,3,4,5,6);      }      public static void show(String str,int... arr)//...就表示可变参数      {          System.out.println(arr.length);      }  

(七)、静态导入
①写法:
import staticjava.util.Arrays.*;//导入的是Arrays这个类中的所以静态成员。
import staticjava.lang.System.*//导入了Ssytem类中所以静态成员。
没加static导入的是类,加上static导入的全是某一个类中所以的静态成员。这样写在调用该类的静态方法时可以不用再写类名。如:Arrays.sort(数组);就可以直接写sort(数组);
②注意:
当导入的两个类中有同名成员时,需要在成员前加上相应的类名。
当类名重名时,需要指定具体的包名。当方法重名时,指定具体所属的对象或者类。

0 0