黑马程序员——013——JavaAPI⑤(集合框架(Map)、Collections)

来源:互联网 发布:linux下编译c程序 编辑:程序博客网 时间:2024/05/20 19:49
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

Map集合
我们在回头来看看我们Java集合类关系图:
这里写图片描述
左中部分已经基本被我们学习完了,现在轮到右边了。
这一节我们学习Map集合,Collection和Map都可以算是集合当中的顶层接口:
—他们外观没联系,实际上内部还是有联系的。
—Map下面也有一些子类对象,我们查看API文档;
这里写图片描述
——发现它的泛型看起来很叼的样子;K→key键,V→value值;
——k和v之间有映射关系;一个键映射一个值域;
—Map集合:该集合存储键值对,一对一对的往里面存的。因此我们知道需要保证键的唯一性。
—相较于ArrayList的加索引index,map就相当于起名字;
—因此我们可以形象的想象为:Collection是单列的,Map是双列的;
—Map集合的方法:
——①添加
———V put(K key, V value)
————将指定的值与此映射中的指定键关联(可选操作)。
———void putAll(Map

Map<String,String> map = new HashMap<String,String>();//——————————————————————//添加元素map.put("01","lisi1");map.put("02","lisi2");map.put("03","lisi3");//put方法在对旧键替换新值的时候,返回旧值,下第二句打印lisi1System.out.println(map.put("01","lisi2"));//打印"lisi1"//——————————————————————/*判断*///判断key存在System.out.println(map.containsKey("00"));//打印falseSystem.out.println(map.containsKey("01"));//打印true//判断value存在System.out.println(map.containsKey("lisi0"));//打印falseSystem.out.println(map.containsKey("lisi1"));//打印true//使用get方法,get(键)System.out.println(map.get("00")==true);//打印falseSystem.out.println(map.get("01")==true);//打印true//但是要注意,HashMap可以存空,因此不好用,不过一般谁也不会存空值吧:map.put("04",null);//存入控null值System.out.println(map.get("04"));//打印null//——————————————————————//获取map集合中所有的值。Collection<String> c = map.values();System.out.println(coll);System.out.println(map);

———————————————————————————
如果想要取出所有的键值,map集合的两种取出方式:
—①keySet方法;
——将map中所有的键存入到set集合。因为set具备迭代器。
——将所有可以迭代方式取出所有的键,在根据get方法,获取每一个键对应的值。
—②entrySet方法;将map集合的映射关系存入到了set集合中,而这个关系的数据类型就是:Map.Entry;
—我们用一个实例代码来演示两种取出方式的用法:
———————————————————————————

import java.util.*;class Demo13_1{        public static void main(String[] args)        {                /*通过keySet方法,获取map集合键值*/                func1();                /*通过entrySet方法,获取map集合键值*/                func2();        }        /*通过keySet方法,获取map集合键值*/        static void func1()        {                Map<String,String> map = new HashMap<String,String>();                map.put("01","halo1");                map.put("02","halo2");                map.put("03","halo3");                map.put("04","halo4");                //仅获取map集合的所有键的Set集合,keySet()                Set<String> keySet = map.keySet();                //有了键的set集合,就可以遍历获取到键值了                Iterator<String> it = keySet.iterator();                while(it.hasNext())                {                        //获取键值                        String key = it.next();                        //键值获取到了就可以通过它使用get方法获取值了                        String value = map.get(key);                        System.out.println("key:"+key+"...value:"+value);                }        }        /*通过entrySet方法,获取map集合键值*/        static void func2()        {                Map<String,String> map = new HashMap<String,String>();                map.put("01","halo1");                map.put("02","halo2");                map.put("03","halo3");                map.put("04","halo4");                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();                        //通过Map.Entry的getKey方法和getValue方法获取键值                        String key = me.getKey();                        String value = me.getValue();                        System.out.println(key+":"+value);                }        }}

———————————————————————————
因此我们可以总结:
—Map集合的取出原理,将Map集合转成set集合,在通过迭代器取出;
——①keySet方法;
———将map中所有的键存入到set集合。因为set具备迭代器。
———将所有可以迭代方式取出所有的键,在根据get方法,获取每一个键对应的值。
——②entrySet方法;将map集合的映射关系存入到了set集合中,而这个关系的数据类型就是:Map.Entry;
——Map.Entry:其实Entry和Map一样,也是一个接口,它是Map接口中的一个内部接口;
——因此,先有Map集合才有Entry关系→内部类
——我们从API文档出也能看出来了,因为只有在内部类才能使用static修饰,如下图:
这里写图片描述
因此我们可以想到,源代码中一定存在以下代码逻辑:
———————————————————————————

/*接口Map源代码*/interface Map{        ......        public static interface Entry//等待被实现的Entry接口        {                ......                public abstract Object getKey();                public abstract Object getValue();                ......        }        ......}/*哈希Map源代码*/class HashMap implements Map{        ......        class Hahs implements Map.Entry//实现接口        {                ......                //实现接口方法                public Object getKey(){...}                public Object getValue(){...}                ......        }        ......}

———————————————————————————
我们使用下面的代码来演示Map集合的使用:
需求:每一个学生都有对应的归属地,学生Student,地址String;姓名和年龄相同则视为同一个学生,要保证学生的唯一性;
思路:
—①描述学生;
—②定义Map容器,将学生作为键,地址作为值,存入;
—③获取Map集合中的元素;
———————————————————————————

import java.util.*;class Demo13_2{        public static void main(String[] args)        {                HashMap<Student,String> hm = new HashMap<Student,String>();                hm.put(new Student("halo04",33),"xinjiang");                hm.put(new Student("halo02",53),"beijing");                hm.put(new Student("halo03",22),"guangdong");                hm.put(new Student("halo01",24),"haerbin");                //hm.put(new Student("halo03",22),"wuhan");                //第一种遍历HashMap方法使用keySet方法                        Set<Student> keySet = hm.keySet();                Iterator<Student> it_keySet = keySet.iterator();                while(it_keySet.hasNext())                {                        Student s = it_keySet.next();                        String addr = hm.get(s);                        System.out.println(s+".."+addr);                }                System.out.println("---------------");                //第二种遍历HashMap方法使用entrySet方法                        Set<Map.Entry<Student,String>> entrySet = hm.entrySet();                Iterator<Map.Entry<Student,String>> it_entry = entrySet.iterator();                while(it_entry.hasNext())                {                        Map.Entry<Student,String> entry = it_entry.next();                        Student s = entry.getKey();                        String addr = entry.getValue();                        System.out.println(s+".."+addr);                }        }}/*定义学生类,实现Comparable接口,让其具备比较性;*/class Student extends Person implements Comparable<Person>{        private String name;        private int age;        public Student(String name,int age)        {                this.name = name;                this.age = age;        }        //覆盖equals方法        public boolean equals(Object obj)        {                if(!(obj instanceof Student))                        throw new ClassCastException("类型不匹配!");                Student s = (Student)obj;                return this.name.equals(s.getName())                        && this.age == (s.getAge());        }        //覆盖hashCode方法        public int hashCode()        {                return this.name.hashCode()+this.age*27;        }        //实现接口的compareTo方法        public int compareTo(Person p)        {//Comparable接口,为了防止有需求要往二叉树里面存                if(!(p instanceof Student))                        throw new ClassCastException("类型不匹配!");                int result = this.name.compareTo(p.getName());                if(result==0)                {                        if(this.age>p.getAge())                                return 1;                        else if(this.age==p.getAge())                                return 0;                        return -1;                }                return result;        }        //覆盖toString方法        public String toString()        {                return name+":"+age;        }}/*定义人类*/class Person{        private String name;        private int age;        public String getName()        {                return this.name;        }        public int getAge()        {                return this.age;        }}

———————————————————————————
执行结果:
这里写图片描述
经验:一个类的对象可能涉及多种存储的时候,最好最好是↓
—①实现Comparable接口,实现compareTo方法,让对象天生可比性、可排序;
—②重写hashCode方法以及equals方法,保证对象能够往哈希表里面放;
———————————————————————————
我们再用一个实例演示TreeMap的使用:
需求:对上例中的学生对象进行按年龄升序排序;
思路:因为数据是以键值对形式存在的,一个学生对象对应一个地址,因此我们不能够使用List或者Set的单列集合,必须使用Map,然后又知道是需要排序的,因此我们就使用TreeSet;
———————————————————————————

import java.util.*;class Demo13_3{        public static void main(String[] args)        {                Map<Student,String> map = new TreeMap<Student,String>(new StudentComparator());                map.put(new Student("halo1",23),"哈尔滨");                map.put(new Student("halo2",21),"北京");                map.put(new Student("halo3",10),"广东");                map.put(new Student("halo4",21),"湖南");                map.put(new Student("halo1",10),"广东");                //使用entrySet方法获取键值                Set<Map.Entry<Student,String>> entrySet = map.entrySet();                Iterator<Map.Entry<Student,String>> it = entrySet.iterator();                while(it.hasNext())                {                        Map.Entry<Student,String> entry = it.next();                        Student s = entry.getKey();                        String addr = entry.getValue();                        System.out.println(s+"--"+addr);                }        }}/*由于学生类中的排序方式不是我们想要的,那么,我们需要按照一个新的规则来排序,因此,如TreeSet一样,我们可以使用比较器*/class StudentComparator implements Comparator<Person>{        public int compare(Person p1,Person p2)        {                //如果不是学生的实例对象,抛RuntimeException的子类ClassCastException                if(!(p1 instanceof Student))                {                        throw new ClassCastException("类型不匹配!请传入一个学生类的实例");                }                if(!(p2 instanceof Student))                {                        throw new ClassCastException("类型不匹配!请传入一个学生类的实例");                }                Integer age1 = new Integer(p1.getAge());                Integer age2 = new Integer(p2.getAge());                int compareResult = age1.compareTo(age2);                if(compareResult == 0)                {                        return p1.getName().compareTo(p2.getName());                }                return compareResult;        }}/*定义学生类,实现Comparable接口,让其具备比较性;*/class Student extends Person implements Comparable<Person>{        private String name;        private int age;        public Student(String name,int age)        {                            super(name,age);                this.name = name;                this.age = age;        }        //覆盖equals方法        public boolean equals(Object obj)        {                if(!(obj instanceof Student))                        throw new ClassCastException("类型不匹配!");                Student s = (Student)obj;                return this.name.equals(s.getName())                        && this.age == (s.getAge());        }        //覆盖hashCode方法        public int hashCode()        {                return this.name.hashCode()+this.age*27;        }        //实现接口的compareTo方法        public int compareTo(Person p)        {//Comparable接口,为了防止有需求要往二叉树里面存                if(!(p instanceof Student))                        throw new ClassCastException("类型不匹配!");                int result = this.name.compareTo(p.getName());                if(result==0)                {                        if(this.age>p.getAge())                                return 1;                        else if(this.age==p.getAge())                                return 0;                        return -1;                }                return result;        }        //覆盖toString方法        public String toString()        {                return name+":"+age;        }}/*定义人类*/class Person{        private String name;        private int age;                public Person(String name,int age)                {                        this.name = name;                        this.age = age;                }        public String getName()        {                return this.name;        }        public int getAge()        {                return this.age;        }}

———————————————————————————
我们再来看一个TreeSet的练习:
需求:“sdfgz/-/-xcva#!@df”获取该字符串中的英文字母出现的次数;
—打印结果如“a(次数)b(次数)c(次数)…”abc按顺序的格式;
分析:每一个字母对应一个次数,因此,这个练习中的字母和对象是有映射关系的,因此我们使用Map集合,而又有需要打印结果按照abcd…顺序,因此需要排序,又有键值对有需要排序,不用多想,就是用TreeSet集合,看代码:
———————————————————————————

import java.util.*;class Demo13_4{        public static void main(String[] args)        {                String s = "sdfgz/*-/*-xcva#$%sdfxcv@#$!@df";                //对字母一个个操作,因此首先转成char数组                char[] chs = s.toCharArray();                //建立一个Map容器,                //char泛型要写成包装类Character                //int也是,Integer                Map<Character,Integer> map = new TreeMap<Character,Integer>();                //循环判断每一个chs的元素                for(int i=0;i<chs.length;i++)                {                        //当不是英文字母的时候,跳过这次循环                        if(!((chs[i]<='z'&&chs[i]>='a') || (chs[i]<='Z'&&chs[i]>='A')))                                continue;                        Integer value = map.get(chs[i]);                        if(value==null)                                value = 0;                        map.put(chs[i],value + 1);                }                //按照格式打印出结果                Set<Map.Entry<Character,Integer>> set= map.entrySet();                Iterator<Map.Entry<Character,Integer>> it = set.iterator();                while(it.hasNext())                {                        Map.Entry<Character,Integer> entry = it.next();                        Character c = entry.getKey();                        Integer i = entry.getValue();                        System.out.print(c+"("+i+")");                }                //打印结果:a(1)c(2)d(3)f(3)g(1)s(2)v(2)x(2)z(1)        }}

———————————————————————————
打印结果:a(1)c(2)d(3)f(3)g(1)s(2)v(2)x(2)z(1)
———————————————————————————
Map扩展
Map集合被使用是因为具备映射关系;
—比如说一个学校,有很多教室,一间教室有很多学生一样;
—这时候就可以这么定义集合:
——首先是学校:
———Map

import java.util.*;class Demo13_5{        public static void main(String[] args)        {                //定义String对象的List集合容器                List<String> list = new ArrayList<String>();                list.add("dcca");                list.add("bcafwa");                list.add("akkkk");                list.add("yyyy");                list.add("aca");                list.add("z");                sop(list);//[dcca, bcafwa, akkkk, yyyy, aca, z]                //1.按照字母自然顺序排序                //由于了String天生实现了Comparable接口                Collections.sort(list);//因此可以直接将集合传进来,不用使用sort两个参数的方法                sop(list);//[aca, akkkk, bcafwa, dcca, yyyy, z]                //2.按照字母长度排序                //需要用到比较器了                list.add("aba");//添加多一个为了验证同等长度再按照字母自然顺序排                Collections.sort(list,new StringLengtComparator());                sop(list);//[z, aba, aca, dcca, yyyy, akkkk, bcafwa]        }        public static void sop(Object obj)        {                System.out.println(obj);        }}/*按照字母长度排序的规则比较器*/class StringLengtComparator implements Comparator<String>{        public int compare(String s1,String s2)        {                int result = new Integer(s1.length()).compareTo(new Integer(s2.length()));                if(result==0)                        return s1.compareTo(s2);                return result;        }}

———————————————————————————
Collections之max方法
—返回List集合中自然顺序排序的最后的一个元素;
—或者返回List集合中比较器顺序排序的最后的一个元素;
此方法我们看一个实例就能够明白其作用了:
——————————————————————

import java.util.*;class Demo13_6{        public static void main(String[] args)        {                //定义String对象的List集合容器                List<String> list = new ArrayList<String>();                list.add("dcca");                list.add("bcafwa");                list.add("akkkk");                list.add("yyyy");                list.add("zz");                list.add("aca");                list.add("z");                //不使用比较器                String max = Collections.max(list);                sop(max);//打印自然顺序最大的串 zz                //使用比较器                String lengthMax = Collections.max(list,new StringLengtComparator());                sop(lengthMax);//打印长度最大的串 bcafwa        }        public static void sop(Object obj)        {                System.out.println(obj);        }}/*按照字母长度排序的规则比较器,略,同上例代码*/

———————————————————————————
Collections之binarySearch方法
binarySearch方法:使用二分法搜索指定List集合。
—当然也是有两个方法,一个不用比较器,一个用比较器:
——static int binarySearch(List

import java.util.*;class Demo13_7{        public static void main(String[] args)        {                //定义String对象的List集合容器                List<String> list = new ArrayList<String>();                list.add("dcca");                list.add("bcafwa");                list.add("akkkk");                list.add("yyyy");                list.add("zz");                list.add("aca");                list.add("z");                //按照自然顺序排序后查找                Collections.sort(list);                sop(list);//查看排序后结果                //使用自写的二分法查找baa串                int index = halfSearch(list,"baa");                sop("自写的index="+index);//3                //使用官方提供的二分法查找baa串                index = Collections.binarySearch(list,"baa");                sop("官方的index="+index);//3                //按照长度排序后查找                Collections.sort(list,new StringLengtComparator());                sop(list);//查看按照长度排序后结果                //使用自写的二分法查找baa串                index = halfSearch2(list,"baa",new StringLengtComparator());                sop("自写的index="+index);                //使用官方提供的二分法查找baa串                index = Collections.binarySearch(list,"baa",new StringLengtComparator());                sop("官方的index="+index);        }        public static void sop(Object obj)        {                System.out.println(obj);        }        /*自写的使用比较器的二分法查找*/        public static int halfSearch2(List<String> list,String key,Comparator<String> comp)        {                int max,min,mid=0;                max = list.size()-1;min = 0;                while(max>=min)//                {                        mid = (max+min)>>1;//右移n位相当于除以2的n次方                        String midStr = list.get(mid);                        int result = comp.compare(key,midStr);                        if(result>0)                                min = mid+1;                        else if(result<0)                                max = mid-1;                        else                                return mid;                }                return -min-1;                //这里的返回值既要能够表现出找不到                //也能够表现出其能够插入的位置                //因此设置成 (-(插入点) - 1)。        }        /*自写的不使用比较器的二分法查找*/        public static int halfSearch(List<String> list,String key)        {                int max,min,mid=0;                max = list.size()-1;min = 0;                while(max>=min)//                {                        mid = (max+min)>>1;//右移n位相当于除以2的n次方                        String midStr = list.get(mid);                        int result = key.compareTo(midStr);                        if(result>0)                                min = mid+1;                        else if(result<0)                                max = mid-1;                        else                                return mid;                }                return -min-1;                //这里的返回值既要能够表现出找不到                //也能够表现出其能够插入的位置                //因此设置成 (-(插入点) - 1)。        }}/*按照字母长度排序的规则比较器,略,同上上例代码*/

———————————————————————————
理解代码的最关键部位就是理解为什么min是如果没有找到的时候最终的插入点。
—可以用一个实即的数字实例来画一下,就很好理解了;
———————————————————————————
Collections之fill方法
使用指定元素替换指定列表中的所有元素。
—不用多说,一个实例代码就能够说明问题:
———————————————————————————

import java.util.*;class Demo13_8{        public static void main(String[] args)        {                List<String> list = new ArrayList<String>();                list.add("abcd");                list.add("aaa");                list.add("ccc");                list.add("zz");                list.add("kkk");                System.out.println(list);                //将集合中的元素全部换成"halo"                Collections.fill(list,"halo");                System.out.println(list);        }}

———————————————————————————
执行结果:
这里写图片描述
思考:如果只要某一段元素需要替换怎么办?
———————————————————————————
Collection之replaceAll方法
replaceAll的API描述:
—public static boolean replaceAll(List list, T oldVal, T newVal)
——将list集合的oldVal替换成newVal;
实例代码:
———————————————————————————

import java.util.*;class Demo13_9{        public static void main(String[] args)        {                List<String> list = new ArrayList<String>();                list.add("abcd");                list.add("aaa");                list.add("ccc");                list.add("zz");                list.add("kkk");                System.out.println(list);                //将集合中的元素全部换成"halo"                Collections.replaceAll(list,"aaa","halo");                System.out.println(list);        }}

———————————————————————————
这里写图片描述
———————————————————————————
Collection之reverse方法
根据单词我们就知道是反转的意思了,实例代码:
———————————————————————————

import java.util.*;class Demo13_10{        public static void main(String[] args)        {                List<String> list = new ArrayList<String>();                list.add("abcd");                list.add("aaa");                list.add("ccc");                list.add("zz");                list.add("kkk");                System.out.println(list);                //将集合中的元素全部换成"halo"                Collections.reverse(list);                System.out.println(list);        }}

———————————————————————————
执行结果:
这里写图片描述
———————————————————————————
Collections之reverseOrder方法
有两个:
—①空参数方法,能够提供一个针对自然顺序反序排序,也就是返回一个倒序排序的比较器;
—②重载方法,有一个参数,能够传入一个比较器作为参数,并且按照比较器的顺序,返回一个倒序排序的比较器;
实例代码:
———————————————————————————

import java.util.*;class Demo13_11{        public static void main(String[] args)        {                //按照字母自然顺序排序的TreeSet集合                //TreeSet<String> set                //        = new TreeSet<String>();                //按照字母自然顺序倒序排序的TreeSet集合                //TreeSet<String> set                //        = new TreeSet<String>(Collections.reverseOrder());                //按照字母长度顺序倒序排序的TreeSet集合                TreeSet<String> set                        = new TreeSet<String>(Collections.reverseOrder(new StringLengthComparator()));                set.add("hasdfa");                set.add("dfwadfa");                set.add("a");                set.add("haa");                set.add("hasa");                sop(set);        }        public static void sop(Object obj)        {                System.out.println(obj);        }}/*按照字母长度排序的规则比较器,略,上面已经写过了*/

———————————————————————————
Collections之synchronizedList方法
集合中那么多对象都是线程不安全的,那么如果多线程操作如何呢?
—Collections这个工具类就能够提供“给我不安全集合,返回你安全集合”的方法;
—————————————————————
这里写图片描述
—————————————————————
我们如果查看源码:可以发现其添加和删除方法都使用了同步代码块,加了同一个锁”mutex”:
这里写图片描述
所以结论就是:Collections中的方法能够让集合从非同步变成同步;
———————————————————————————
Collections之swap、reverse和shuffle方法
swap方法:可以替换list集合中的两个位置的元素:
—static void swap(List

0 0
原创粉丝点击