黑马程序员——011——JavaAPI③(集合框架(List之LinkedList)、(Set之HashSet)

来源:互联网 发布:广州易娱网络主管 编辑:程序博客网 时间:2024/05/10 03:05
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

LinkedList
—还是和前面的小节一样,我们这里介绍其特有的方法:
——void addFirst(E e)
———将指定元素插入此列表的开头。
——void addLast(E e)
———将指定元素添加到此列表的结尾。
——E getFirst()
———返回此列表的第一个元素。
——E getLast()
———返回此列表的最后一个元素。
——E removeFirst()
———移除并返回此列表的第一个元素。
——E removeLast()
———移除并返回此列表的最后一个元素。
—实例代码:
———————————————————————————

import java.util.*; class Demo11_1{        public static void main(String[] args)        {                func1();                func2();                func3();        }        /*演示addFirst方法*/        static void func1()        {                LinkedList linkedList = new LinkedList();                //演示addFirst,getFirst方法                linkedList.addFirst("halo1");                linkedList.addFirst("halo2");                linkedList.addFirst("halo3");                linkedList.addFirst("halo4");                System.out.println(linkedList);                System.out.println("linkedList.size():"+linkedList.size());//4                System.out.println(linkedList.getFirst());//打印halo4                System.out.println("linkedList.size():"+linkedList.size());//大小没有变化                //由于每个都往开头插入                //因此最终打印[halo4, halo3, halo2, halo1]        }        /*演示addLast方法*/        static void func2()        {                LinkedList linkedList = new LinkedList();                //演示addFirst方法                linkedList.addLast("halo1");                linkedList.addLast("halo2");                linkedList.addLast("halo3");                linkedList.addLast("halo4");                System.out.println(linkedList);                System.out.println("linkedList.size():"+linkedList.size());//4                System.out.println(linkedList.getLast());//打印halo4                System.out.println("linkedList.size():"+linkedList.size());//4大小没有变化                //由于每个都往尾部插入                //因此最终打印[halo1, halo2, halo3, halo4]        }        /*演示removeFirst,removeLast方法*/        static void func3()        {                LinkedList linkedList = new LinkedList();                linkedList.addLast("halo1");                linkedList.addLast("halo2");                linkedList.addLast("halo3");                linkedList.addLast("halo4");                System.out.println("linkedList.size():"+linkedList.size());                System.out.println("linkedList.removeFirst():"+linkedList.removeFirst());//打印halo1                System.out.println(linkedList);//[halo2, halo3, halo4]                System.out.println("linkedList.size():"+linkedList.size());//剩下3个                System.out.println("linkedList.removeFirst():"+linkedList.removeLast());//打印halo4                System.out.println(linkedList);//[halo2, halo3]                System.out.println("linkedList.size():"+linkedList.size());//剩下2个        }}

———————————————————————————
运行结果:
这里写图片描述
我们能够从中发现getFirst/getLast方法和removeFirst/removeLast方法的区别:
—两种都能够获取元素,
——但getFirst/getLast方法获取元素,不删除集合中元素;
——removeFirst/removeLast方法获取元素,会删除集合中元素;
——因此我们或许可以这样来清空集合:
———————————————————————————
while(!linkedList.isEmpty())
{
linkedList.removeFirst();
//或linkedList.removeLast();
}
我们可以看下面的实例:
———————————————————————————

import java.util.*;class Demo11_2{        public static void main(String[] args)        {                LinkedList linkedList = new LinkedList();                //演示addFirst,getFirst方法                linkedList.addFirst("halo1");                linkedList.addFirst("halo2");                linkedList.addFirst("halo3");                linkedList.addFirst("halo4");                while(!linkedList.isEmpty())                {                        System.out.println(linkedList.removeLast());                }                //对已经空的集合使用removeFirst或removeLast方法                //执行取出第一或最后一个操作                System.out.println(linkedList.removeLast());                //抛出java.util.NoSuchElementException异常        }}

———————————————————————————
由于对已经空的集合使用removeFirst或removeLast方法会抛出异常,同理在getFirst或getLast方法上也会有相同的问题,因此在Java1.6开始,Java出现了替代的方法:
pollFirst()或pollLast()方法;
——代替removeFirst或removeLast方法,当对已经空的集合使用pollFirst()或pollLast()方法会返回null,而不抛异常;
peekFirst()或peekLast()方法;
——代替getFirst或getLast方法,当对已经空的集合使用peekFirst()或peekLast()方法;会返回null,而不抛异常;
下面再看一个练习实例代码:使用LinkedList模拟堆栈和队列数据结构;
———————————————————————————

import java.util.*;/*使用LinkedList模拟堆栈和队列数据结构;*/class Demo11_3{        public static void main(String[] args)        {                //队列,先进先出                                sop("队列,先进先出");                DuiLie duilie = new DuiLie();                duilie.myAdd("java01");                duilie.myAdd("java02");                duilie.myAdd("java03");                duilie.myAdd("java04");                sop(duilie);                while(!duilie.isNull())                {                        sop(duilie.myGet());                }                //堆栈,后进先出                                sop("堆栈,后进先出");                DuiZhan duiZhan = new DuiZhan();                duiZhan.myAdd("java01");                duiZhan.myAdd("java02");                duiZhan.myAdd("java03");                duiZhan.myAdd("java04");                sop(duiZhan);                while(!duiZhan.isNull())                {                        sop(duiZhan.myGet());                }        }        public static void sop(Object obj)        {                System.out.println(obj);        }}/*模拟队列数据结构*/class DuiLie{        private LinkedList link = null;        public DuiLie()        {                link = new LinkedList();        }        public void myAdd(Object obj)        {                this.link.addFirst(obj);        }        public Object myGet()        {                return this.link.removeLast();//先进先出        }        public boolean isNull()        {                return link.isEmpty();        }        public String toString()        {                return link.toString();        }}/*模拟堆栈数据结构*/class DuiZhan{        private LinkedList link = null;        public DuiZhan()        {                link = new LinkedList();        }        public void myAdd(Object obj)        {                this.link.addFirst(obj);        }        public Object myGet()        {                return this.link.removeFirst();//先进后出        }        public boolean isNull()        {                return link.isEmpty();        }        public String toString()        {                return link.toString();        }}

———————————————————————————
ArrayList和LinkedList如何选择:
—我们需要看需求:当需要频繁插入增删操作的时候,使用LinkedList;
涉及到频繁的查询首选ArrayList操作;
———————————————————————————
———————————————————————————
Set
现在我们就要接触Collection体系的Set体系了,首先我们现在再来总结一下Collection体系:
———————————————————————————
Collection
|–List:元素是有序的,元素可以重复,因为该集合体系有索引;
|–ArrayList:底层的数据结构使用的是数据结构;特点:查询速度快,但是增删稍慢,并且是线程不同步的;
|–LinkedList:底层使用的是链表数据结构;特点;增删速度快,查询稍慢;
|–Vector:底层的数据结构使用的是数据结构;但是是线程同步的,从Java1.2开始被ArrayList代替。
|–Set:元素是无序的(存入取出的顺序不一定一致),元素不可重复;
———————————————————————————
这里写图片描述
———————————————————————————
从体系图我们知道Set是Collection下一员,因此其功能和Collection是一致的。
—Set是个接口,其子类有又很多,我们这里涉及两个HasHSet和TreeSet;
我们还需要了解一下哈希表的概念:
—哈希表:
——哈希表是按照哈希值来存的;
——哈希值一样再用equals方法比较是不是同一个对象;
——每个对象直接打印的时候,如果没有重写toString方法,打印出来的都是哈希值;
———如果我们查看Object的toString方法,我们可以看到:
————getClass().getName() + ‘@’ + Integer.toHexString(hashCode())
————这就是我们有时候直接打印对象的时候,出来一些我们看不懂东西的原因了。
———————————————————————————
我们要理解Set体系中无序和重复元素为什么不再添加的原因,首先看下面的实例,我们首先以Set的实现类HashSet为例:
———————————————————————————

import java.util.*;class Demo11_4{        public static void main(String[] args)        {                HashSet hs = new HashSet();                sop(hs.add("halo1"));//打印true                //添加多一个halo1,发现添加失败                sop(hs.add("halo1"));//打印false                sop(hs);//只有halo1一个        }        static void sop(Object obj)        {System.out.println(obj);}}———————————————————————————我们也可以添加自定义元素,既然是自定义元素,我们必然需要表明怎么样的自定义元素是一样的,我们先看下面实例代码:———————————————————————————import java.util.*;class Demo11_5{        public static void main(String[] args)        {                HashSet set = new HashSet();                set.add(new Person("lisi",20));                set.add(new Person("lisi",21));                set.add(new Person("lisi",30));                sop(set.add(new Person("lisi",20)));//false,因为上面同名同年龄的已经放进来了                set.add(new Person("lisi",21));//添加失败,因为上面同名同年龄的已经放进来了                Iterator it = set.iterator();                while(it.hasNext())                {                        Person p = (Person)it.next();                        sop(p.getName()+".."+p.getAge());                }                //做了五次add,最后只有三个元素                sop(set.size());//3        }        public static void sop(Object obj)        {                System.out.println(obj);        }}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;        }        /*重写hashCode方法*/        public int hashCode()//重写hashCode和equals方法        {                //保证返回的结果对于每个对象都能够保证唯一                return this.name.hashCode()+this.age*37;        }        /*重写equals方法*/        public boolean equals(Object obj)        {                if(!(obj instanceof Person))//如果不是Person类的对象,                        return false;//就必然是不一样的对象了,直接false                Person p = (Person)obj;                //返回名字和年龄的比较的与的结果                return p.getName().equals(this.name)&&p.getAge()==this.age;        }}

———————————————————————————
HashSet集合判断自定义元素的是否一致的依据:
—首先判断hashCode的返回值是否一致,一致的话在判断equals是否返回true,hashCode一致&&equals方法返回true,则两个自定义元素是相同的。hashCode不一致,则不再判断equals方法,两个自定义元素是不同的
因此程序中建立了一个有针对性的哈希值,这样比较好,因为如果直接返回一个固定值,那就相当于每个元素的hash值都一样了,那么每次都会调用equals方法,非常麻烦。
因此,hashSet集合保证元素唯一性的依据就是①hashCode和②equals方法
要注意的小细节,如下图:
这里写图片描述
重写这两个方法的时候注意字母大小写以及参数类型,否则覆盖失败的时候很麻烦,导致开发者以为我明明重写了为什么执行结果不是我想要的,其实没有重写成功
———————————————————————————
HashSet判断和删除的依据
—使用HashSet的contains方法和remove方法后发现:
——他们俩也是依赖hashCode和equals方法,先hashCode方法,后equals方法,hashCode方法都得到不同结果的话,就不验证equals方法了。
——而ArrayList中的所有的都只依赖于equals方法;
——这都是由数据结构决定的!
—结论:所以在哈希表中想要删除添加,都需要首先判断哈希值;
———————————————————————————
现在我们可以再完善一下体系的总结了:
—————————————————————
Collection
|–List:元素是有序的,元素可以重复,因为该集合体系有索引;
|–ArrayList:底层的数据结构使用的是数据结构;特点:查询速度快,但是增删稍慢,并且是线程不同步的;
|–LinkedList:底层使用的是链表数据结构;特点;增删速度快,查询稍慢;
|–Vector:底层的数据结构使用的是数据结构;但是是线程同步的,从Java1.2开始被ArrayList代替。
|–Set:元素是无序的(存入取出的顺序不一定一致),元素不可重复;
|–HashSet:底层数据结构是哈希表,无序;线程非同步。保证元素唯一性的原理:判断元素的hashCode值是否相同,同则继续判断equals方法true或false,true则两个元素重复。
|–TreeSet:可以对Set集合中的元素进行排序,详见下文。
———————————————————————————
———————————————————————————
TreeSet
—TreeSet可以弥补Set集合无序的不足;
—先来个小Demo:
———————————————————————————

import java.util.*;class Demo11_6{        public static void main(String[] args)        {                //新建一个TreeSet集合                TreeSet ts = new TreeSet();                ts.add("cba");                ts.add("aaa");                ts.add("bca");                ts.add("abcd");                //遍历打印集合                Iterator it = ts.iterator();                while(it.hasNext())                {                        System.out.print(it.next()+" ");                }        }}

———————————————————————————
打印结果:
aaa abcd bca cba
——————————
我们发现结果是按照字母的顺序排序了,ASCII码小的排了前面。
于是我们需要思考当存进去的是自定义对象的时候,排序的规则又是什么呢?
—这里直接告诉大家就是这个规则就是“Comparable接口”。
—如果我们查看String类的API我们会发现其实现了“Comparable接口”的,因此上面的实例中我们看到了其按照自然顺序排序。
—至于实现了“Comparable接口”之后还需要做什么事情呢?请看下面的实例代码:
———————————————————————————

import java.util.*;class Demo11_7{        public static void main(String[] args)        {                /*                //为工人类建立容器                TreeSet ts_w = new TreeSet();                ts_w.add(new Wokrer("halo01",23));                ts_w.add(new Wokrer("halo01",23));//抛异常,因为Worker没有实现Comparable接口                */                //为学生类建立容器                TreeSet ts_s = new TreeSet();                ts_s.add(new Student("halo02",23));                ts_s.add(new Student("halo04",21));                ts_s.add(new Student("halo01",25));                ts_s.add(new Student("halo03",22));                ts_s.add(new Student("halo06",25));                ts_s.add(new Student("halo05",25));                Iterator it = ts_s.iterator();                while(it.hasNext())                        sop(it.next());        }        static void sop(Object obj)        {                System.out.println(obj);        }}class Person{        private String name;        private int age;        public Person(String name,int age)        {                this.name = name;                this.age = age;        }        public String toString()        {                return name+"-"+age;        }        public String getName()        {                return this.name;        }        public int getAge()        {                return this.age;        }        public int hashCode()        {                System.out.println("hashCode被执行了");                return this.name.hashCode()+this.age*37;        }        public boolean equals(Object obj)        {                System.out.println("equals被执行了");                if(!(obj instanceof Person))                        return false;                Person person = (Person)obj;                return this.name.equals(person.getName());        }}class Worker extends Person{        private String name;        private int age;        public Worker(String name,int age)        {                super(name,age);                this.name = name;                this.age = age;        }}class Student extends Person implements Comparable{        private String name;        private int age;        public Student(String name,int age)        {                super(name,age);                this.name = name;                this.age = age;        }        public int compareTo(Object obj)        {                if(!(obj instanceof Student))                        throw new RuntimeException("非法的存入对象!");//不需throws声明的异常                Student s = (Student)obj;                int compareResult = new Integer(this.age).compareTo(new Integer(s.getAge()));                if(compareResult==0)                        return this.name.compareTo(s.getName());                return compareResult;        }}

———————————————————————————
执行结果:
这里写图片描述
TreeSet集合能够排序,但是现在还不知道排序的规则,规则就是让放进去的对象具备排序性;也就是需要找到Comparable接口实现它,然后就具备比较性了。
这就是为什么我们执行“ts_w.add(new Wokrer(“halo01”,23));”会抛异常的原因。那么实现接口需要实现compareTo这个方法。
—————————————————
当年龄一样的时候,若此时让compareTo方法返回0,代表两个对象相等,那么这个对象就会没有被存进去。
要注意的是TreeSet集合并不会调用对象的hashCode和equals方法,我们这里重写了也等于白写,不过一般谁也不知道元素对象会被存入什么集合,因此重写hashCode和equals方法以及实现Comparable接口,几乎是开发中标配工作。
因此需要再按照姓名再排一下序,而姓名字符串String在Java中已经实现了Comparable接口了,如下图:
这里写图片描述
所以需要记住,使用TreeSet排序时候,要注意次要条件,当主要条件相同的时候,还要根据次要条件来判断是否是同一个元素,次要不等于没必要。
注意:Java中很多的类自身都具备了比较性,也就是都实现了这个接口;
—比如,int的包装类Integer,我们代码中对Student的年龄使用了Integer来包装了一下,为了调用Integer的compareTo方法;
———————————————————————————
———————————————————————————
二叉树
由于排序可能需要频繁的比较,因此可能会比较慢,因此TreeSet底层用了一个特殊的数据结构来优化——二叉树;
还是按照上一实例中代码,我们画个图来解释TreeSet数据结构存入数据的过程:
这里写图片描述
①以第一个存入的元素为根节点;
②存入第二个元素,首先比较年龄,小的往根节点的左边放;
③存入第三个元素,首先和根节点比较年龄,小的往根节点的左边放,在和根节点左边已有元素比较年龄,小的继续的往左边放;
④同理;
⑤存入第五个元素,首先和根节点比较年龄,大的往根节点的右边放;
⑥同理……
二叉树遍历的规则:
—从”左下→上→右下”的原则;
——比如下面的二叉树的遍历顺序为:GDHBIEJACKF
这里写图片描述
———————————————————————————
如果要降序,直接使用一个方法就可以了,不需要修改compareTo方法,下文中会涉及到;
如果相要按照怎么存进去就怎么取出来的话,只需要把compareTo方法,return 1;即可;
—1>0,表示新元素总是大于旧元素。
———————————————————————————
那么现在,我们又可以完善一下Collection体系中关于Set的体系了:
Collection
|–List:元素是有序的,元素可以重复,因为该集合体系有索引;
|–ArrayList:底层的数据结构使用的是数据结构;特点:查询速度快,但是增删稍慢,并且是线程不同步的;
|–LinkedList:底层使用的是链表数据结构;特点;增删速度快,查询稍慢;
|–Vector:底层的数据结构使用的是数据结构;但是是线程同步的,从Java1.2开始被ArrayList代替。
|–Set:元素是无序的(存入取出的顺序不一定一致),元素不可重复;
|–HashSet:底层数据结构是哈希表,无序;线程非同步。保证元素唯一性的原理:判断元素的hashCode值是否相同,同则继续判断equals方法true或false,true则两个元素重复。
|–TreeSet:可以对Set集合中的元素进行排序,底层数据结构是二叉树,保证了元素唯一性的依据是:compareTo方法,和hashCode和equals方法无关。
———————————————————————————
以上所涉及的是TreeSet排序的第一种方式:让元素自身具备比较性;元素需要实现Comparable接口覆盖compareTo方法;以这种方式也称为元素的自然顺序,或者叫做默认顺序。
TreeSet集合第二种排序方法:开发中我们可能拿到的是元素对象,而这个元素对象的类并不是我们负责的源代码编写的,因此当类不是自己写的,没有实现Comparable接口,也就是需要存入的元素不具备比较性时,又或者具备的比较性(实现了Comparable接口),但是其实现的compareTo方法不是所需要的,怎么排序列?
答案是使用比较器Comparator,Comparator也是“java.util”包中的一个接口;
我们查看TreeSet的构造函数可以发现其中有一个带参数的重载构造方法:
—TreeSet(Comparator

import java.util.*;class Demo11_8{        public static void main(String[] args)        {                //为工人类建立容器                TreeSet ts_w = new TreeSet(new WorkerComparator());                ts_w.add(new Worker("halo01",26));                ts_w.add(new Worker("halo01",23));                ts_w.add(new Worker("halo05",26));                ts_w.add(new Worker("halo01",22));                Iterator it = ts_w.iterator();                while(it.hasNext())                        sop(it.next());        }        static void sop(Object obj)        {                System.out.println(obj);        }}/*为工人排序专门定义的比较器通过实现接口中的方法告诉别人这个比较器以什么规则排序*/class WorkerComparator implements Comparator{        /*实现接口的compare方法*/        public int compare(Object o1,Object o2)        {                System.out.println("compare被执行");                //当o1或者o2的类型不是worker的时候,让程序停止                if(!(o1 instanceof Worker))                        throw new RuntimeException(o1+"类型错误");                if(!(o2 instanceof Worker))                        throw new RuntimeException(o2+"类型错误");                Worker w1 = (Worker)o1;                Worker w2 = (Worker)o2;                int ageCompareResult = new Integer(w1.getAge())                        .compareTo(new Integer(w2.getAge()));                if(ageCompareResult==0)                        //当年龄相同的时候比较按照姓名顺序排序,                        return w1.getName().compareTo(w2.getName());                        //名字还相同的就会当成一个元素不往集合中存了                return ageCompareResult;        }}/*Person类略,同上代码*/

———————————————————————————
我们再用一个实例代码,来演示比较器的用法:
—需求:按照字符串长度排序。
—思路:我们知道String是已经实现了Comparable接口的,但是是按照自然排序的,因此不是我们需要的,我们要使用比较器来让其按照字符串长度排序;
———————————————————————————

import java.util.*;class Demo11_9{        public static void main(String[] args)        {                TreeSet ts = new TreeSet(new StringLengthComparator());                ts.add("abc");                ts.add("dbc");                ts.add("abbbc");                ts.add("aabc");                ts.add("v");                System.out.println(ts);                //打印[v, abc, dbc, aabc, abbbc]        }}class StringLengthComparator implements Comparator{        public int compare(Object o1,Object o2)        {                //当类型不是String类型的时候抛异常                if(!(o1 instanceof String))                        throw new RuntimeException(o1+"的类型不是String");                if(!(o2 instanceof String))                        throw new RuntimeException(o2+"的类型不是String");                String s1 = (String)o1;                String s2 = (String)o2;                if(s1.length()>s2.length())                        //当s1长于s2,表示大于s2,需要排在后,因此返回大于0的值                        return 1;                else if(s1.length()<s2.length())                        //当s1短于s2,表示小于s2,需要排在前,因此返回小于0的值                        return -1;                else                        //当s1和s2长度相同的时候,按照字母自然顺序排序                        return s1.compareTo(s2);        }}
0 0
原创粉丝点击