Set、Map

阅读: 评论:0

Set、Map

Set、Map

(一)Queue接口和Deque接口
1、Queue(队列)接口的实现类通常是以 FIFO(先进先出)的方式排序各个元素。
2、队列的实现类有很多,后续在JavaEE中会给大家介绍。
今天只看LinkedList。
LinkedList它不仅是List接口的实现类,从这个角度来说,把双向链表当成一个普通的序列/列表来使用的。
          它又是Queue接口的实现类,从这个角度来说,把双向链表当成一个队列来使用。


刚才Stack是Vector的子类,它为了体现“先进后出”的栈结构特点,增加了一些方法:peek,pop,push等方法。
那么LinkedList为了体现“先进先出”的队列结构的特点,从Queue接口中继承/实现了一些方法:
        抛出异常        返回特殊值
插入      add(e)          offer(e)
移除      remove()        poll()
检查      element()       peek()

左边的add,remove,element方法
        当添加时,遇到那种有容量限制的队列的话,添加失败就报异常,
        在删除和判断队头元素时,队列已经空了,也报异常。
右边的offer,poll,peek方法
        当添加时,遇到那种有容量限制的队列的话,添加失败就返回值特殊值给用户判断,
        在删除和判断队头元素时,队列已经空了,返回null。

3、Deque接口是Queue接口的子接口,它是双端队列。
    名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。
    Queue接口是单向队列,添加到添加队尾,删除从队头删除。
    Deque接口是双端队列,可以往队头添加,也可以往队尾添加,删除也可以删除队头元素,也可以删除队尾元素。
    Deque接口的实现类代表有LinkedList。

        第一个元素(头部)           最后一个元素(尾部)
         抛出异常    特殊值            抛出异常        特殊值
插入  addFirst(e)   offerFirst(e)     addLast(e)      offerLast(e)
移除  removeFirst() pollFirst()       removeLast()    pollLast()
检查   getFirst()   peekFirst()       getLast()       peekLast()


4、双端队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留 Stack 类。
  在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出。堆栈方法完全等效于 Deque 方法,如下表所示:

堆栈方法    等效 Deque 方法
push(e)     addFirst(e)
pop()       removeFirst()
peek()      peekFirst()


总结:LinkedList集众多的数据结构于一身。
(1)普通的列表,调用List接口的API方法
(2)单向队列,调用Queue接口的API方法
(3)双端队列,调用Deque接口的API方法
(4)栈结构,调用 栈结构的方法,push,pop等,或者用Deque方法代替

public class TestQueue {@Testpublic void test06() {//演示LinkedList作为栈结构使用LinkedList list = new LinkedList();list.addFirst("hello");list.addFirst("world");System.out.veFirst());//world 后进先出System.out.veFirst());//helloSystem.out.veFirst());//NoSuchElementException}@Testpublic void test05() {//演示LinkedList作为栈结构使用LinkedList list = new LinkedList();list.push("hello");list.push("world");System.out.println(list.pop());//world 后进先出System.out.println(list.pop());//helloSystem.out.println(list.pop());//NoSuchElementException}@Testpublic void test04() {//演示LinkedList作为双端队列结构使用LinkedList list = new LinkedList();list.addFirst("hello");list.addFirst("world");list.addLast("chailinyan");list.addFirst("atguigu");list.addFirst("java");System.out.First());//javaSystem.out.veFirst());//javaSystem.out.veLast());//chailinyan}@Testpublic void test03(){//演示LinkedList作为队列结构使用//add(e)和remove()配合使用,体现了队列的“先进先出”的特点LinkedList list = new LinkedList();list.add("hello");list.add("world");list.add("java");System.out.println(list.poll());//注意,这个remove()不带参数System.out.println(list.poll());//注意,这个remove()不带参数System.out.println(list.poll());//注意,这个remove()不带参数System.out.println(list.poll());//不报异常NoSuchElementException,而是返回null}@Testpublic void test02(){//演示LinkedList作为队列结构使用//add(e)和remove()配合使用,体现了队列的“先进先出”的特点LinkedList list = new LinkedList();list.add("hello");list.add("world");list.add("java");System.out.println(list.element());//查看队头的元素helloSystem.out.println(list.element());//查看队头的元素helloSystem.out.println(list.element());//查看队头的元素helloSystem.out.println(list.element());//查看队头的元素hello}@Testpublic void test01(){//演示LinkedList作为队列结构使用//add(e)和remove()配合使用,体现了队列的“先进先出”的特点LinkedList list = new LinkedList();list.add("hello");list.add("world");list.add("java");System.out.ve());//注意,这个remove()不带参数System.out.ve());//注意,这个remove()不带参数System.out.ve());//注意,这个remove()不带参数System.out.ve());//报异常NoSuchElementException没有元素}
}

(二)Set接口
1、java.util.Set接口的集合的特点:元素不能重复。
2、Set接口是Collection的子接口,本身没有扩展Collection接口的方法。
Collection的方法,仍然适用于适用于Set接口,
Collection的遍历方式,也仍然适用于适用于Set接口。

3、Set接口的实现类:
HashSet:无序的元素不可重复的
LinkedHashSet:它是HashSet的子类,可以保证元素的添加顺序,但是也不可重复。
            把添加到LinkedHashSet的元素,用结点包装,用链表把它们连接起来。
TreeSet:元素是有大小顺序的,不可重复。


问:如何选择HashSet和LinkedHashSet?
如果你对元素的添加顺序不关心的话,选择HashSet即可。
如果你需要记录元素的添加顺序的话,选择LinkedHashSet。它的效率比HashSet略低。

4、这些Set的实现类,是如何判断元素是否重复的呢?
LinkedHashSet和HashSet:hashCode()值和equals()方法
        如果我们没有重写元素类型的hashCode()值和equals()方法,那么equals就和==比较一样,比较对象的地址。4
        如果我们重写元素类型的hashCode()值和equals()方法,那么就用我们重写的比较规则。

        先判断hashCode是否一样,如果不一样,就不用判断equals了。
                            如果hashCode一样,就再判断equals。

        问:为什么要先判断hashCode,hashCode一样的时候,再判断equals,为什么不直接判断equals呢?
                                 hashCode不一样时,为什么不判断equals呢?

           回忆:翻笔记
           hashCode()和equals方法一定要一起重写,并且要遵循如下原则:
            A:如果两个对象的equals方法调用结果是true,那么它俩的hashCode值一定要一样
            B:如果两个对象的hashCode值不同,那么equals方法一定要返回false
            C:如果两个对象的hashCode值相同,那么equals方法的结果可能是true,可能是false
                    例如:"Aa"和"BB"字符串 的hashCode值相同,但是equals却是false
                    例如:"Aa"和"Aa"字符串 的hashCode值相同,equals也是true

            这里不直接判断equals的原因:
                因为equals方法往往需要比较对象的很多属性值,那么是很麻烦的事情。(效率低)
                而hashCode值是一个int的数字,比较两个int值很快,如果hashCode值不同,就省略了equals比较。(效率高)
                理论上,不同的对象,hashCode不同的概率更高。所以可以省略大量的equals比较过程。

TreeSet:
    要求元素实现java.lang.Comparable接口,重写int compareTo(Object obj)方法。
    如果两个元素调用compareTo方法比较大小时,返回0,就认为它俩是重复对象,就添加不成功。

    凡是Java中和对象比较大小有关的,都是有两种选择,要么选择元素对象实现Comparable接口,要么编写类实现java.util.Comparator接口

    结论:
    要添加到TreeSet集合中的元素,要么实现Comparable接口,重写int compareTo(Object obj)方法。
                            要么在创建TreeSet集合对象时,传入Comparator接口的实现类对象。
                            它们的作用是用来比较集合元素对象的大小,和保证元素不可重复。

public class TestSet {@Testpublic void test07(){//演示如果元素没有实现Comparable接口,重写int compareTo(Object obj)方法,// 也可以给TreeSet指定一个java.util.Comparator接口的实现类对象,以保证元素按照大小顺序排列,并且不可重复TreeSet set = new TreeSet(new Comparator(){@Overridepublic int compare(Object o1, Object o2) {return Doublepare(((Circle)o1).getRadius(),((Circle)o2).getRadius());}});set.add(new Circle(2));set.add(new Circle(1));set.add(new Circle(2));set.add(new Circle(3));set.add(new Circle(1));for (Object o : set) {System.out.println(o);}}@Testpublic void test06(){//演示TreeSet依赖于元素实现Comparable接口,重写int compareTo(Object obj)方法,以保证元素按照大小顺序排列,并且不可重复TreeSet set = new TreeSet();//如果Rectangle类型没有实现Comparable接口,重写int compareTo(Object obj)方法,就会异常,// 因为在元素添加过程中,需要比较两个对象的大小,就要调用compareTo方法比较大小,就需要转换为Comparable类型//如果Rectangle类型没有实现Comparable接口,那么类型转换为Comparable类型时,就会报ClassCastException异常set.add(new Rectangle(3,2));set.add(new Rectangle(3,1));set.add(new Rectangle(3,2));//失败set.add(new Rectangle(5,1));set.add(new Rectangle(5,1));//失败set.add(new Rectangle(6,1));//失败  compareTo方法比较面积,面积相等,就认为重复元素了//成功 compareTo方法先比较length,length一样再看widthfor (Object o : set) {System.out.println(o);}}@Testpublic void test05(){//用Circle类型的对象演示HashSet的一个不可重复问题HashSet set = new HashSet();set.add(new Circle(1));set.add(new Circle(1));set.add(new Circle(2));set.add(new Circle(2));System.out.println("------------");//如果Circle没有重写hashCode()值和equals()方法,set中有4个对象//如果Circle重写hashCode()值和equals()方法,set中有2个对象//foreach循环快捷键iterfor (Object o : set) {System.out.println(o);}}@Testpublic void test04(){//演示TreeSet的元素按照大小顺序排列,不可重复TreeSet set = new TreeSet(); //TreeSet接口的一个实现类set.add("hello");set.add("hello");set.add("java");set.add("world");set.add("atguigu");System.out.println(set);//[atguigu, hello, java, world]}@Testpublic void test03(){//演示LinkedHashSet,不可重复,可以保证元素的添加顺序LinkedHashSet set = new LinkedHashSet(); //LinkedHashSet是Set接口的一个实现类set.add("hello");set.add("hello");set.add("java");set.add("world");set.add("atguigu");System.out.println(set);//[hello, java, world, atguigu]}@Testpublic void test02(){//演示HashSet无序,不可重复HashSet set = new HashSet(); //HashSet是Set接口的一个实现类set.add("hello");set.add("hello");set.add("java");set.add("world");set.add("atguigu");System.out.println(set);//[java, world, atguigu, hello]}@Testpublic void test01(){//Set集合的元素不能重复List list = new ArrayList();//ArrayList是List接口的实现类list.add("hello");list.add("hello");System.out.println(list);//[hello, hello]Set set = new HashSet(); //HashSet是Set接口的一个实现类set.add("hello");set.add("hello");System.out.println(set);//[hello]}
}class Circle{private double radius;public Circle(double radius) {this.radius = radius;}public double getRadius() {return radius;}public void setRadius(double radius) {this.radius = radius;}@Overridepublic String toString() {return "Circle{" +"radius=" + radius +'}';}@Overridepublic boolean equals(Object o) {System.out.println("circle的equals");if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Circle circle = (Circle) o;return Doublepare(circle.radius, radius) == 0;}@Overridepublic int hashCode() {System.out.println("circle的hashCode");return Objects.hash(radius);}
}class Rectangle implements Comparable{private double length;private double width;public Rectangle(double length, double width) {this.length = length;this.width = width;}public double getLength() {return length;}public void setLength(double length) {this.length = length;}public double getWidth() {return width;}public void setWidth(double width) {this.width = width;}@Overridepublic String toString() {return "Rectangle{" +"length=" + length +", width=" + width +'}';}public double area(){return length * width;}@Overridepublic int compareTo(Object o) {//假设矩形对象按照面积比较大小
//        return Doublepare(this.area(), ((Rectangle)o).area());//如果不按照面积比较,可以按照长和宽的大小比较Rectangle other =  (Rectangle)o;int result = Doublepare(this.length , other.length);if(result == 0){result = Doublepare(this.width, other.width);}return result;}
}

集合分为两大类:Collection(一组对象)、Map(键值对)
Collection系列:
List子接口:ArrayList、LinkedList、Vector、Stack
Set子接口:HashSet、TreeSet、LinkedHashSet

Collection系列:遍历
foreach循环
Iterator iterator()
Object[] toArray

(三) Map系列
1、java.util.Map接口
(1)添加
Object put(Object key, Object value):添加一对键值对
void putAll(Map m)  :添加多对键值对,相当于把m的集合中的键值对全部添加到当前集合中

(2)删除
Object remove(Object key) :根据key可以删除一对键值对
void clear()  :清空

(3)查询
boolean containsKey(Object key):用来查找某个key是否存在
boolean containsValue(Object value)  :用来查找某个value是否存在
Object get(Object key) :根据key返回value
boolean isEmpty()
int size()

(4)修改
Object put(Object key, Object value):再次添加,如果key和原来的一对键值对的key重复,那么就会修改原来的value。

(5)遍历
Set keySet():返回所有的key,然后遍历
Collection values():返回所有的 value,然后遍历
Set entrySet() :返回所有的键值对(key,value),然后遍历

思考:Set和Collection是接口,接口是不能new对象的,那么它返回的是什么对象呢?
    答:Set和Collection接口的实现类在 Map的具体类型中用“内部类”来表示。
    例如:HashMap中用KeySet内部类来实现了Set接口,
                    Values内部类实现了Collection接口。


2、Map系列的集合的特点:
(1)value可以重复
(2)key不可以重复
(3)value可以修改,但是key不能修改

class MyKey{int k;//故意不私有化,这样可以简化下面的操作public MyKey(int k) {this.k = k;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;MyKey myKey = (MyKey) o;return k == myKey.k;}@Overridepublic int hashCode() {return Objects.hash(k);}@Overridepublic String toString() {return "MyKey{" +"k=" + k +'}';}
}public class TestMap {@Testpublic void test11() {//演示Map的遍历,entrySet()方法Map map = new HashMap();//HashMap是Map接口的一个实现类map.put("张长友", "古力娜扎");map.put("付宇杰", "如花");Set set = Set(); //返回的是所有键值对,而Map中所有的键值对都是Entry类型//这里的Entry是Map接口中的一个内部接口//这个接口的实现类是在Map的具体类型中用“内部类”形式表示//例如:在HashMap中用Node内部类实现了Map.Entry接口//因为所有键值对的key不能重复,也就意味着(key,value)的键值对Entry对象不能重复,所以用set集合返回。for (Object o : set) {System.out.println(o);}}@Testpublic void test10() {//演示Map的遍历,values()方法Map map = new HashMap();//HashMap是Map接口的一个实现类map.put("张长友", "古力娜扎");map.put("付宇杰", "如花");Collection values = map.values();//得到所有的value  因为value可能重复,就不用set了//因为所有的value现在放到一个Collection系列的集合中,可以使用foreach等遍历for (Object value : values) {System.out.println(value);}}@Testpublic void test09() {//演示Map的遍历,keySet()方法Map map = new HashMap();//HashMap是Map接口的一个实现类map.put("张长友", "古力娜扎");map.put("付宇杰", "如花");Set set = map.keySet(); //返回map中所有的key  map的所有key不能重复,set集合的特点也是元素不能重复//在keySet()方法中,创建了一个Set集合对象,并且把所有的key放进去//接下来可以遍历set//因为Set是Collection系列的,可以使用foreach等遍历方式for (Object o : set) {System.out.println(o);}}@Testpublic void test08(){Map map = new HashMap();//key,我们设计一个自己的类型,叫做MyKeyMyKey my = new MyKey(1);map.put(my, "一个value而已");//关注key//尝试修改keymy.k = 2;System.out.(my));//根据my这个key,重新获取一下它的valueSystem.out.println(map);}@Testpublic void test07() {//演示Map的添加putMap map = new HashMap();//HashMap是Map接口的一个实现类map.put("张长友", "古力娜扎");map.put("付宇杰", "如花");map.put("张长友","如花");System.out.println(map);}@Testpublic void test05() {//演示Map的添加putMap map = new HashMap();//HashMap是Map接口的一个实现类map.put("张长友", "古力娜扎");map.put("付宇杰", "如花");System.out.("付宇杰"));}@Testpublic void test04() {//演示Map的添加putMap map = new HashMap();//HashMap是Map接口的一个实现类map.put("张长友", "古力娜扎");map.put("付宇杰", "如花");System.out.ainsKey("张长友"));System.out.ainsValue("如花"));}@Testpublic void test03(){//演示Map的添加putMap map = new HashMap();//HashMap是Map接口的一个实现类map.put("张长友", "古力娜扎");map.put("付宇杰", "如花");ve("张长友");System.out.println(map);}@Testpublic void test02(){//演示Map的添加putAllMap map = new HashMap();//HashMap是Map接口的一个实现类map.put("张长友", "古力娜扎");map.put("付宇杰", "如花");Map map2 = new HashMap();//HashMap是Map接口的一个实现类map2.put("许仙", "白娘子");map2.put("Jack", "Rose");map.putAll(map2);System.out.println(map);}@Testpublic void test01(){//演示Map的添加putMap map = new HashMap();//HashMap是Map接口的一个实现类map.put("张长友", "古力娜扎");map.put("付宇杰", "如花");System.out.println(map);}
}

(四)Map接口的实现类
(1)HashMap:哈希表
(2)TreeMap:按照key的大小顺序排列的
(3)LinkedHashMap:HashMap的子类
(4)Hashtable:哈希表
(5)Properties:它是Hashtable的子类,

问:HashMap和Hashtable的区别?
Hashtable属于最古老的哈希表。它是线程安全的。 不允许key和value为null值
HashMap属于比Hashtable新的哈希表,它是线程不安全的。允许key和value为null。

问:TreeMap的key有什么要求?
 key对应的类型必须实现java.lang.Comparable接口
 或者
 在创建TreeMap时,传入java.util.Comparator接口实现类对象(可以匿名内部类实现)

问:HashMap和 LinkedHashMap的区别?
    HashMap是无序的,
    LinkedHashMap:按照添加顺序排列的

    LinkedHashMap中(key,value)的Entry的实现类,维护了一个链表结构。

问:Properties有什么特殊的?
它是用于存储系统属性的map,它的key和value是String类型的。
它的添加(put)和获取(get)建议用setProperty()和getProperty()代替。

public class TestMapImpl {@Testpublic void test11(){//演示读取jdbc.properties文件中的配置信息Properties properties = new Properties();try {properties.load(ClassLoader().getResourceAsStream("jdbc.properties"));//这句的作用是加载/读取jdbc.properties文件中的配置信息} catch (IOException e) {e.printStackTrace();}System.out.Property("username"));System.out.Property("url"));}@Testpublic void test10(){Properties properties = Properties();//获取系统属性Set entries = Set();for (Object entry : entries) {System.out.println(entry);}}@Testpublic void test09(){Properties properties = new Properties();properties.setProperty("username","chai");properties.setProperty("password","123456");System.out.Property("username"));}@Testpublic void test08(){//演示 HashMap是无序的,LinkedHashMap map = new LinkedHashMap();map.put("zhangsan", "曹林");map.put("lisi", "汪飞");map.put("chailinyan", "许圆圆");System.out.println(map);//{zhangsan=曹林, lisi=汪飞, chailinyan=许圆圆}}@Testpublic void test07(){//演示 HashMap是无序的,HashMap map = new HashMap();map.put("zhangsan", "曹林");map.put("lisi", "汪飞");map.put("chailinyan", "许圆圆");System.out.println(map);//{lisi=汪飞, zhangsan=曹林, chailinyan=许圆圆}}@Testpublic void test06(){//演示 HashMap是无序的,HashMap map = new HashMap();map.put("张三", "曹林");map.put("李四", "汪飞");map.put("柴林燕", "许圆圆");System.out.println(map);}@Testpublic void test05(){TreeMap tree = new TreeMap(new Comparator(){@Overridepublic int compare(Object o1, Object o2) {Student s1 = (Student) o1;Student s2 = (Student) o2;Id() - s2.getId();}});tree.put(new Student(1,"张三",89), "value1");tree.put(new Student(2,"李四",78), "value2");Set set = Set();for (Object o : set) {System.out.println(o);}}@Testpublic void test04(){TreeMap tree = new TreeMap();//这样写,如果Student没有实现java.lang.Comparable接口会报错,ClassCastExceptiontree.put(new Student(1,"张三",89), "value1");tree.put(new Student(2,"李四",78), "value2");Set set = Set();for (Object o : set) {System.out.println(o);}}@Testpublic void test03(){TreeMap tree = new TreeMap();tree.put(3, "wednesday");tree.put(1, "monday");tree.put(4, "thursday");//这里的key是Integer类型Set set = Set();for (Object o : set) {System.out.println(o);}}@Testpublic void test02(){HashMap map = new HashMap();map.put("陈宇哲",null);System.out.println(map);//{陈宇哲=null}map.put(null,null);System.out.println(map);}@Testpublic void test01(){Hashtable table = new Hashtable();table.put("陈宇哲",null);//NullPointerException}
}class Student{private int id;private String name;private int score;public Student() {}public Student(int id, String name, int score) {this.id = id;this.name = name;this.score = score;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getScore() {return score;}public void setScore(int score) {this.score = score;}@Overridepublic String toString() {return "Student{" +"id=" + id +", name='" + name + ''' +", score=" + score +'}';}
}

问?Map和Set有什么关系?
   或者
   Set的内部和Map什么关系?

答:Set的内部是用Map实现的。
  HashSet内部用的是HashMap
  TreeSet内部用的是TreeMap
  LinkedHashSet内部用的是LinkedHashMap

  添加到Set中的是一个一个的元素,而Map要的是一对一对的,怎么把一个变成一对的?
     添加到set中的元素是作为底层map的key使用的,
     map的value是用了一个Object类型的常量对象PRESENT,

     这里的PRESENT是这样声明的:
     private static final Object PRESENT = new Object();

     static:所有对象共享
     final:常量不可变

     相当于所有的HashSet中的元素,底层的map(key,value)的value是共享了共一个常量对象,节省了内存。

本文发布于:2024-01-28 03:20:46,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/17063832504417.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:第13章
下一篇:第13章约束
标签:Set   Map
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23