java-栈、队列、数组、链表、Hash、树以及集合(二)

来源:互联网 发布:德国统一 知乎 编辑:程序博客网 时间:2024/06/08 08:31

引言
主要整理了集合的另一个部分,Set,Set是一个接口,它的实现类有hashSet和treeSet,下面主要介绍有关这两个实现类。
HashSet
元素顺序:元素唯一,但是无序(它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变)
用一段代码进行测试:

package com.stu.hashset;import java.util.HashSet;//案例:创建一个HashSet集合,添加元素(String元素),测试唯一性,无序性public class HashSetStu01 {    public static void main(String[] args) {        HashSet<String> hs = new HashSet<String>();        hs.add("hello");        hs.add("world");        hs.add("java");        hs.add("world");        for(String str:hs){            System.out.println(str);        }    }}

编译运行结果如下:
hello
java
world
那么,hashSet是如何保证元素的唯一性呢?HashSet集合保证元素的唯一性和add()方法相关。如果我们想深入的了解,就必须看add()方法的源码,看它的底层依赖什么内容?
在这里,做一个简单的介绍:
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {…}
左边:e.hash == hash 比较对象的哈希值。
右边:((k = e.key) == key || key.equals(k))
(1)左边:(k = e.key) == key
比较对象的地址值。
(2)右边:key.equals(k)
比较的是对象的内容是否相同。默认情况下比较的是地址值
结论:
底层数据结构是哈希表。
哈希表依赖两个方法:hashCode()和equals()
执行流程:
首先判断哈希值是否相同,如果不同,就直接添加到集合。
如果相同,继续执行equals(),看其返回值,
如果是false,就直接添加到集合。
如果是true,说明元素重复不添加。
使用:
如果你看到哈希结构的集合,就要考虑可能需要重写这两个方法。
如果真要重写,自动生成即可。
举一个简单的例子做一说明:

//学生类package com.stu.hashset;public class Student {    private String name;    private String age;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getAge() {        return age;    }    public void setAge(String age) {        this.age = age;    }    public Student(String name, String age) {        super();        this.name = name;        this.age = age;    }    public Student() {        super();        // TODO Auto-generated constructor stub    }    //重写hashcode和equals    @Override    public int hashCode() {        final int prime = 31;        int result = 1;        result = prime * result + ((age == null) ? 0 : age.hashCode());        result = prime * result + ((name == null) ? 0 : name.hashCode());        return result;    }    @Override    public boolean equals(Object obj) {        if (this == obj)            return true;        if (obj == null)            return false;        if (getClass() != obj.getClass())            return false;        Student other = (Student) obj;        if (age == null) {            if (other.age != null)                return false;        } else if (!age.equals(other.age))            return false;        if (name == null) {            if (other.name != null)                return false;        } else if (!name.equals(other.name))            return false;        return true;    }}

编译运行结果如下:
周杰伦 35
方大同 35
薛之谦 35
用一个图解对上述做一个简单的说明:
这里写图片描述
TreeSet
元素顺序:使用元素的自然顺序对元素进行排序,或者根据创建 set时提供的 Comparator进行排序(比较器排序),具体取决于使用的构造方法。
底层算法:二叉树,如图所示:
这里写图片描述
元素要求, 加入自定义JavaBean
用一个案例进行理解:创建集合存储Integer类型的元素(20,18,23,22,17,24,19,18,24),分析如何保证元素唯一性,以及排序的规则。二叉树数据结构是怎么存入元素的,根据compareTo()方法的返回值来确定要存入元素的安放位置
具体代码实现如下:

package com.stu.treeset;import java.util.TreeSet;/*创建集合存储Integer类型的元素(20,18,23,22,17,24,19,18,24),分析如何保证元素唯一性,以及排序的规则。二叉树(首先讲解二叉树数据结构是怎么存入元素的,根据compareTo()方法的返回值来确定要存入元素的安放位置)*/public class TreeSetStu01 {    public static void main(String[] args) {        TreeSet<Integer> ts = new TreeSet<Integer>();        //(20,18,23,22,17,24,19,18,24)        ts.add(20);        ts.add(18);        ts.add(23);        ts.add(22);        ts.add(17);        ts.add(24);        ts.add(19);        ts.add(18);        ts.add(24);        for(Integer integer:ts){            System.out.println(integer);        }       }}

编译运行结果如下:
17
18
19
20
22
23
24
当我们自定义一个对象的时候,就需要重写comparato方法,否则无法完成比较,举一个案例来实现:
存入学生对象(姓名,年龄),1.按年龄排序,2.姓名排序(自然排序实现Comparable接口,并重写comparaTo()方法)

//学生javabean    package com.stu.treeset;/*存入学生对象(姓名,年龄),    1.按年龄排序,    2.姓名排序(自然排序实现Comparable接口,并重写comparaTo()方法)*/public class Student implements Comparable<Student>{    private String name;    private int 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;    }    public Student(String name, int age) {        super();        this.name = name;        this.age = age;    }    public Student() {        super();        // TODO Auto-generated constructor stub    }    @Override    public int compareTo(Student s) {        //按照学生的年龄进行排序        /**         * 两个对象进行比较:         * s         * this         */        int num=this.age-s.age;        //判断年龄是否相同,如果相同比较姓名     int    num2=(num==0?this.name.compareTo(s.name):num);        return num2;    }    @Override    public String toString() {        return "Student [name=" + name + ", age=" + age + "]";    }}//测试类package com.stu.treeset;import java.util.TreeSet;public class TreeSetStu03 {    public static void main(String[] args) {        TreeSet<Student> ts = new TreeSet<Student>();        Student s1 = new Student("薛之谦",35);        Student s2= new Student("方大同",32);        Student s3 = new Student("汪涵",40);        Student s4 = new Student("周杰伦",35);        ts.add(s1);        ts.add(s2);        ts.add(s3);        ts.add(s4);        for(Student st:ts){            System.out.println(st);        }    }}

编译运行后结果如下:
Student [name=方大同, age=32]
Student [name=周杰伦, age=35]
Student [name=薛之谦, age=35]
Student [name=汪涵, age=40]
HashSet与TreeSet的相同点与不同点:
相同点:
单列集合,元素不可重复
不同点
1. 底层存储的数据结构不同
HashSet底层用的是HashMap哈希表结构存储,而TreeSet底层用的是TreeMap树结构存储
2.存储时保证数据唯一性依据不同
HashSet是通过复写hashCode()方法和equals()方法来保证的,而TreeSet通过Compareable接口的compareTo()方法来保证的
3.有序性不一样
HashSet无序,TreeSet有序
这种情况的数据,属于一一对应的映射关系。这种关系的集合在java叫Map。
Map:将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
Map接口中的方法概述(创建集合测试方法):
这里写图片描述
有关其功能,用相应代码进行实现:

package com.stu.map;import java.util.Collection;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;import java.util.Set;/* * Map接口中的方法概述(创建集合测试方法): *      A:删除功能 *          void clear():移除集合中的所有键值对元素 *          V remove(Object key):根据键移除键值对元素,并返回值 *      B:判断功能 *          boolean containsKey(Object key):判断集合中是否包含指定的键 *          boolean containsValue(Object value):判断集合中是否包含指定的值 *          boolean isEmpty():判断集合是否为空 *      C:获取功能 *          Set<Map.Entry<K,V>> entrySet():获取键值对对象的集合,遍历键值对对象,            利用getKey(),getValue()取出键和值(理解即可) *                      V get(Object key):根据键获取值 *          Set<K> keySet():获取所有的键 *          Collection<V> values():获取所有的值 *      D:添加功能 *          V put(K key,V value):集合添加键值对 *      E:长度功能 *          int size():键值对对数。 */public class MapStu01 {    public static void main(String[] args) {        //V put(K key,V value):集合添加键值对        Map<Integer, String> hm = new HashMap<Integer,String>();        hm.put(1, "薛之谦");        hm.put(2, "周杰伦");        hm.put(3, "方大同");        hm.put(4, "孙子涵");        //int size():键值对对数        int len =hm.size();        System.out.println(len);//4        //void clear():移除集合中的所有键值对元素        //hm.clear();        /*int len2 =hm.size();        System.out.println(len2);//0*/      //V remove(Object key):根据键移除键值对元素,并返值//      String remove = hm.remove(2);//      System.out.println(remove);//周杰伦        //boolean containsKey(Object key):判断集合中是否包含指定的键//      boolean key = hm.containsKey(3);//      System.out.println(key);//true        //boolean containsValue(Object value):判断集合中是否包含指定的值        boolean value = hm.containsValue("薛之谦");        System.out.println(value);//true        //boolean isEmpty():判断集合是否为空        boolean empty = hm.isEmpty();        System.out.println(empty);//false        //Set<Map.Entry<K,V>> entrySet():获取键值对对象的集合,        //遍历键值对对象,利用getKey(),getValue()取出键和值(理解即可)        Set<Entry<Integer, String>> entrySet = hm.entrySet();        for(Entry<Integer, String> entry:entrySet){            System.out.println(entry.getKey()+" "+entry.getValue());        }        /*         *  1 薛之谦            2 周杰伦            3 方大同            4 孙子涵         */        //Set<K> keySet():获取所有的键        Set<Integer> keySet = hm.keySet();        for(Integer key:keySet){            //V get(Object key):根据键获取值            System.out.println(key+" "+hm.get(key));        }        //Collection<V> values():获取所有的值        Collection<String> values = hm.values();        Iterator<String> it = values.iterator();        while(it.hasNext()){            System.out.println(it.next());        }        /*         *  薛之谦            周杰伦            方大同            孙子涵         */    }}

部分运行结果在代码中注释
HashMap
元素顺序:元素顺序不可预测
底层算法:哈希算法
对键没有要求(仅仅相对于TreeMap来说)
在这里,做一个需求:存入(Student,String),键是同一个对象的时候要把之前存入的元素挤出去,想要实现这样的效果的话,
需要重写javabean里面的hashCode()和equals()方法

//学生javabeanpackage com.edu_10;public class Student{    private String name;    private int age;    public Student(String name, int age) {        super();        this.name = name;        this.age = age;    }    public Student() {        super();        // TODO Auto-generated constructor stub    }    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;    }    @Override    public String toString() {        return "Student [name=" + name + ", age=" + age + "]";    }    @Override    public int hashCode() {        final int prime = 31;        int result = 1;        result = prime * result + age;        result = prime * result + ((name == null) ? 0 : name.hashCode());        return result;    }    @Override    public boolean equals(Object obj) {        if (this == obj)            return true;        if (obj == null)            return false;        if (getClass() != obj.getClass())            return false;        Student other = (Student) obj;        if (age != other.age)            return false;        if (name == null) {            if (other.name != null)                return false;        } else if (!name.equals(other.name))            return false;        return true;    }}//测试package com.edu_10;import java.util.HashMap;import java.util.Set;public class HashMapDemo {    public static void main(String[] args) {        // 3.存入(Student,String)        //创建一个Map集合        HashMap<Student, String> hm = new HashMap<Student, String>();        //创建学生对象        Student s1 = new Student("杰克逊", 60);        Student s2 = new Student("孙楠", 50);        Student s3 = new Student("权志龙", 30);        Student s4 = new Student("权志龙", 30);        //将对象存入集合        hm.put(s1, "美国");        hm.put(s2, "中国");        hm.put(s3, "韩国");        hm.put(s4, "中国");        //遍历集合        Set<Student> keys = hm.keySet();        for (Student s : keys) {            System.out.println(s+"  "+hm.get(s));        }       }}

编译运行后如下:
Student [name=权志龙, age=30] 中国
Student [name=杰克逊, age=60] 美国
Student [name=孙楠, age=50] 中国
Treemap
元素顺序:元素顺序与键的排序规则有关
底层算法:Tree算法
HashMap与TreeMap的相同点与不同点
相同点:主要用于存储键(key)值(value)对,根据键得到值,因此键不允许键重复,但允许值重复。
不同点:
1.HashMap里面存入的键值对在取出的时候是随机的,也是我们最常用的一个Map.根据键可以直接获取它的值,
具有很快的访问速度。在Map 中插入、删除和定位元素,HashMap 是最好的选择。
2.TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。

0 0