Java集合框架(三)Set

Java集合框架(三)Set

Set子接口

  • 特点:无序、无下标、元素不可重复
  • 方法:全部继承Collection中的方法

Set的实现类

  • Set的实现类包括HashSet、TreeSet

  • 举例1:

    //Set特点:无序没有下标、不能重复
    public class Demo01 {
        public static void main(String[] args) {
            //创建集合
            //Set是接口
            Set<String> set = new HashSet<>();
            set.add("苹果");
            set.add("大米");
            set.add("小米");
            set.add("香蕉");
            System.out.println(set.toString());
        }
    }
    //输出(输出顺序与存入顺序是不同的,代表无序):
    [苹果, 香蕉, 大米, 小米]
    
  • 举例2:

    //Set特点:无序没有下标、不能重复
    public class Demo01 {
        public static void main(String[] args) {
            //创建集合
            //Set是接口
            Set<String> set = new HashSet<>();
            set.add("苹果");
            set.add("大米");
            set.add("小米");
            set.add("香蕉");
            System.out.println("--------输出当前数据-----------");
            System.out.println(set.toString());
            System.out.println("----------删除大米----------");
            set.remove("大米");
            System.out.println(set.toString());
            System.out.println("-------遍历1(foreach)--------");
            for (String s : set) {
                System.out.println(s);
            }
            System.out.println("-------遍历2(迭代器)--------");
            Iterator iterator = set.iterator();
            while(iterator.hasNext()){
                System.out.println(iterator.next());
            }
            System.out.println("-------判断是否有小米--------");
            System.out.println(set.contains("小米"));
            System.out.println("-------判断是否为空--------");
            System.out.println(set.isEmpty());
        }
    }
    //输出:
    --------输出当前数据-----------
    [苹果, 香蕉, 大米, 小米]
    ----------删除大米----------
    [苹果, 香蕉, 小米]
    -------遍历1(foreach)--------
    苹果
    香蕉
    小米
    -------遍历2(迭代器)--------
    苹果
    香蕉
    小米
    -------判断是否有小米--------
    true
    -------判断是否为空--------
    false
    

HashSet

  • 基于HashCode实现元素不重复

  • 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入(元素不能重复)

  • HashSet的存储方法:

    1. 根据哈希计算存储的位置是否为空,若为空则直接存储,若不为空则进行第二部
    2. 通过equals判断是否为重复值,如果为true则认为重复,否则,形成链表
  • 可以通过修改hascode方法来存储相同的元素,让hascode在相同的元素每次存储加上一个数,修改存储位置,避免equals导致重复数据无法存入

  • 类中可以重写equals方法,通过重写equals来改变判断的方式

  • 重写equals()必须要先重写hasCode()方法,因为创建两个不同对象但内容相同的元素时,他们默认的hashCode()会让两个对象的下标位置不同,进而当作两个不同的对象存储进去。

    只有修改为通过内容获取哈希值,才能让相同内容的不同对象存在同一下标的链表上,之后才会有equals判断内容是否相等。(举例3)

  • 举例1:

    public class Demo02 {
        public static void main(String[] args) {
            HashSet<String> s1 = new HashSet<>();
            s1.add("香蕉");
            s1.add("苹果");
            s1.add("葡萄");
            s1.add("棒球");
            System.out.println("---------输出结果---------");
            System.out.println("元素个数:"+s1.size());
            System.out.println(s1.toString());
            System.out.println("--------删除----------");
            s1.remove("葡萄");
            System.out.println("--------输出结果----------");
            System.out.println("元素个数:"+s1.size());
            System.out.println(s1.toString());
            System.out.println("--------foreach输出----------");
            for (String s : s1) {
                System.out.println(s);
            }
            System.out.println("--------迭代输出----------");
            Iterator iterator = s1.iterator();
            while (iterator.hasNext()){
                System.out.println(iterator.next());
            }
            System.out.println("--------判断----------");
            System.out.println("是否为空:"+s1.isEmpty());
            System.out.println("是否有棒球:"+s1.contains("棒球"));
        }
    }
    //输出:
    ---------输出结果---------
    元素个数:4
    [香蕉, 苹果, 葡萄, 棒球]
    --------删除----------
    --------输出结果----------
    元素个数:3
    [香蕉, 苹果, 棒球]
    --------foreach输出----------
    香蕉
    苹果
    棒球
    --------迭代输出----------
    香蕉
    苹果
    棒球
    --------判断----------
    是否为空:false
    是否有棒球:true
    
  • 举例2:

    类:

    public class Person {
        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 Person(String name,int age) {
            this.name = name;
            this.age = age;
        }
        public String toString(){
            return this.name+" "+this.age;
        }
    }
    

    主函数:

    public class Demo03 {
        public static void main(String[] args) {
            HashSet<Person> hashSet = new HashSet<>();
            Person p1 = new Person("张三",18);
            Person p2 = new Person("李四",19);
            Person p3 = new Person("王五",20);
            Person p4 = new Person("赵六",16);
            Person p5 = new Person("李四",19);
            hashSet.add(p1);
            hashSet.add(p2);
            hashSet.add(p3);
            hashSet.add(p4);
            hashSet.add(p4);//重复
            hashSet.add(p5);
            //hashSet不会重复存储相同的数据
            //重复指的是存储的哈希值相同,但相同数据内容的不同对象的哈希值不同(因为存储位置不同)
            //相同数据内容的不同对象的不算重复数据(例如p5和p2)
            System.out.println("元素个数:"+hashSet.size());
            System.out.println(hashSet.toString());
            System.out.println("--------删除----------");
            hashSet.remove(p3);//根据哈希值来找,删除
            System.out.println("--------输出结果----------");
            System.out.println("元素个数:"+hashSet.size());
            System.out.println(hashSet.toString());
            //因为重写了toString()方法,所以foreach能正常输出(而不是输出哈希值)
            System.out.println("--------foreach输出----------");
            for (Person person : hashSet) {
                System.out.println(person.toString());
            }
            //因为重写了toString()方法,所以迭代能正常输出(而不是输出哈希值)
            System.out.println("--------迭代输出----------");
            Iterator iterator = hashSet.iterator();
            while (iterator.hasNext()){
                System.out.println(iterator.next());
            }
            System.out.println("--------判断----------");
            System.out.println("是否为空:"+hashSet.isEmpty());
            System.out.println("是否有棒球:"+hashSet.contains("棒球"));
        }
    }
    //输出:
    元素个数:5
    [王五 20, 李四 19, 李四 19, 赵六 16, 张三 18]
    --------删除----------
    --------输出结果----------
    元素个数:4
    [李四 19, 李四 19, 赵六 16, 张三 18]
    --------foreach输出----------
    李四 19
    李四 19
    赵六 16
    张三 18
    --------迭代输出----------
    李四 19
    李四 19
    赵六 16
    张三 18
    --------判断----------
    是否为空:false
    是否有棒球:false
    
  • 举例3(重写equals):

    类(重写equals和hasCode):

    public class Person {
        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 Person(String name,int age) {
            this.name = name;
            this.age = age;
        }
        public String toString(){
            return this.name+" "+this.age;
        }
        /* 重写equals()必须要先重写hasCode()方法:
        因为创建两个不同对象但内容相同的元素时,他们默认的hashCode()会让两个对象的下标位置不同,进而当作两个不同的对象存储进去。
        只有修改为通过内容获取哈希值,才能让相同内容的不同对象存在同一下标的链表上,之后才会有equals判断内容是否相等。
        */
        @Override
        public int hashCode() {
            //根据内容得出哈希值
            //存储时如果哈希值相同,则存到相同下标的链表上
            return Objects.hash(name, age);
        }
        //重写equals
        public boolean equals(Object obj){
            //在同一下标的链表上进行比较
            //this代表调用这个方法的对象的地址(this.equals(Object obj))
            //obj代表这个方法中参数传递过来对象的地址(直接用最大的Object来全覆盖)
            //如果地址相同,则数据肯定相同,不用再往后比较了,直接返回true
            if(this==obj){
                return true;
            }
            //判断是否为空,为空则返回false
            if(obj==null){
                return false;
            }
            //若地址不同,则判断其中的数据内容是否相同
            //若数据内容相同则返回true
            //obj instanceof Person判断obj是否为Person的一个实例,若是,则再比较内容
            if(obj instanceof Person){
                //equals本来是比较地址的,但String重写了equals方法,所以String的equals比较就是在比较值的内容
                Person p = (Person)obj;
                if(this.name.equals(p.getName()) && this.age==p.getAge()){
                    return true;
                }
            }
            //若内容不同,则返回为false
            return false;
        }
    }
    

    主函数:

    public class Demo04 {
        public static void main(String[] args) {
            HashSet<Person> hashSet2 = new HashSet<>();
            Person p1 = new Person("张三",18);
            Person p2 = new Person("李四",19);
            Person p3 = new Person("王五",20);
            Person p4 = new Person("赵六",16);
            Person p5 = new Person("李四",19);
            hashSet2.add(p1);
            hashSet2.add(p2);
            hashSet2.add(p3);
            hashSet2.add(p4);
            hashSet2.add(p4);//重复
            hashSet2.add(p5);
            //由于equals已经重写,所以凡是内容相同或者地址相同的都不能重复存储
            System.out.println("元素个数:"+hashSet2.size());
            System.out.println(hashSet2.toString());
            //删除
            //由于equals和hasCode已经重写,所以可以不用地址,可以通过内容是否相同来删除
            //由于equals和hasCode已经重写,所以下面的删除方式可以用(通过new Person的内容来寻找删除)
            System.out.println("--------删除----------");
            hashSet2.remove(new Person("李四",19));
            System.out.println("--------输出结果----------");
            System.out.println("元素个数:"+hashSet2.size());
            System.out.println(hashSet2.toString());
        }
    }
    //输出:
    元素个数:4
    [张三 18, 赵六 16, 王五 20, 李四 19]
    --------删除----------
    --------输出结果----------
    元素个数:3
    [张三 18, 赵六 16, 王五 20]
    

TreeSet

  • 采用二叉树的方式实现的

  • 基于排列顺序实现元素不重复

  • TreeSet通过查找树存储(左小右大),但当一个数据结构里的属性不只一个时,计算机就不知道拿什么去比较了,这时候就自己告诉计算机按照什么去 比较大小(举例2)

    • 方式1:实现Comparable接口(举例2)

    • 方式2:通过Comparator实现定制比较(比较器)(举例3)

  • 举例1:

    public class Demo05 {
        public static void main(String[] args) {
            TreeSet<String> treeSet = new TreeSet<>();
            treeSet.add("aa");
            treeSet.add("bb");
            treeSet.add("cc");
            treeSet.add("dd");
            System.out.println("---------输出------------");
            System.out.println("元素个数"+treeSet.size());
            System.out.println(treeSet.toString());
            System.out.println("---------删除------------");
            treeSet.remove("bb");
            System.out.println("元素个数"+treeSet.size());
            System.out.println(treeSet.toString());
            System.out.println("---------遍历------------");
            System.out.println("---------foreach------------");
            for (String s : treeSet) {
                System.out.println(s);
            }
            System.out.println("---------迭代------------");
            Iterator iterator = treeSet.iterator();
            while (iterator.hasNext()){
                System.out.println(iterator.next());
            }
        }
    }
    //输出:
    ---------输出------------
    元素个数4
    [aa, bb, cc, dd]
    ---------删除------------
    元素个数3
    [aa, cc, dd]
    ---------遍历------------
    ---------foreach------------
    aa
    cc
    dd
    ---------迭代------------
    aa
    cc
    dd
    
  • 举例2:

    类(重写Comparable接口里的compareTo方法):

    public class Person implements Comparable<Person>{
        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 Person(String name,int age) {
            this.name = name;
            this.age = age;
        }
        public String toString(){
            return this.name+" "+this.age;
        }
        /* 重写equals()必须要先重写hasCode()方法:
        因为创建两个不同对象但内容相同的元素时,他们默认的hashCode()会让两个对象的下标位置不同,进而当作两个不同的对象存储进去。
        只有修改为通过内容获取哈希值,才能让相同内容的不同对象存在同一下标的链表上,之后才会有equals判断内容是否相等。
        */
        @Override
        public int hashCode() {
            //根据内容得出哈希值
            //存储时如果哈希值相同,则存到相同下标的链表上
            return Objects.hash(name, age);
        }
        //重写equals
        public boolean equals(Object obj){
            //在同一下标的链表上进行比较
            //this代表调用这个方法的对象的地址(this.equals(Object obj))
            //obj代表这个方法中参数传递过来对象的地址(直接用最大的Object来全覆盖)
            //如果地址相同,则数据肯定相同,不用再往后比较了,直接返回true
            if(this==obj){
                return true;
            }
            //判断是否为空,为空则返回false
            if(obj==null){
                return false;
            }
            //若地址不同,则判断其中的数据内容是否相同
            //若数据内容相同则返回true
            //obj instanceof Person判断obj是否为Person的一个实例,若是,则再比较内容
            if(obj instanceof Person){
                //equals本来是比较地址的,但String重写了equals方法,所以String的equals比较就是在比较值的内容
                Person p = (Person)obj;
                if(this.name.equals(p.getName()) && this.age==p.getAge()){
                    return true;
                }
            }
            //若内容不同,则返回为false
            return false;
        }
        //实现接口中compareTo的方法
        //原compareTo用来进行比较大小,但因为属性过多计算机不知道比较什么所以要人为重写
        //返回值为0代表重复元素
        @Override
        public int compareTo(Person o) {
            //通过原来的compareTo方法比较是不是同一个人名(同一个人名则返回0)
            int n1=this.getName().compareTo(o.getName());
            //计算年龄差
            int n2=this.age-o.age;
            //如果名字不相同则返回名字比较的大小结果,否则返回年龄的比较大小结果
            //如果n1等于0,则名字相同,这时比较年龄大小(返回年龄差),否则就返回名字比较结果
            //如果n1和n2都为0,则代表是重复元素,在添加时就不能被添加
            return n1==0?n2:n1;
        }
    }
    

    主函数:

    public class Demo06 {
        public static void main(String[] args) {
            TreeSet<Person> treeSet = new TreeSet<>();
            Person p1 = new Person("张三",18);
            Person p2 = new Person("李四",19);
            Person p3 = new Person("王五",20);
            Person p4 = new Person("赵六",16);
            treeSet.add(p1);
            treeSet.add(p2);
            treeSet.add(p3);
            treeSet.add(p4);
            System.out.println("元素个数"+treeSet.size());
            System.out.println(treeSet.toString());
            System.out.println("---------删除1------------");
            treeSet.remove(new Person("李四",19));
            System.out.println("元素个数"+treeSet.size());
            System.out.println(treeSet.toString());
            System.out.println("---------删除2------------");
            treeSet.remove(p3);
            System.out.println("元素个数"+treeSet.size());
            System.out.println(treeSet.toString());
        }
    }
    //输出:
    元素个数4
    [张三 18, 李四 19, 王五 20, 赵六 16]
    ---------删除1------------
    元素个数3
    [张三 18, 王五 20, 赵六 16]
    ---------删除2------------
    元素个数2
    [张三 18, 赵六 16]
    
  • 举例3:

    类:

    public class Person2 {
        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 Person2(String name,int age) {
            this.name = name;
            this.age = age;
        }
        public String toString(){
            return this.name+" "+this.age;
        }
    }
    

    主函数:

    public class Demo07 {
        public static void main(String[] args) {
            //使用匿名内部类
            TreeSet<Person2> treeSet = new TreeSet<>(new Comparator<Person2>() {
                @Override
                public int compare(Person2 o1, Person2 o2) {
                    int n1=o1.getName().compareTo(o2.getName());
                    //计算年龄差
                    int n2=o1.getAge()-o2.getAge();
                    //如果名字不相同则返回名字比较的大小结果,否则返回年龄的比较大小结果
                    //如果n1等于0,则名字相同,这时比较年龄大小(返回年龄差),否则就返回名字比较结果
                    //如果n1和n2都为0,则代表是重复元素,在添加时就不能被添加
                    return n1==0?n2:n1;
                }
            });
            Person2 p1 = new Person2("张三",18);
            Person2 p2 = new Person2("李四",19);
            Person2 p3 = new Person2("王五",20);
            Person2 p4 = new Person2("赵六",16);
            treeSet.add(p1);
            treeSet.add(p2);
            treeSet.add(p3);
            treeSet.add(p4);
            System.out.println("元素个数"+treeSet.size());
            System.out.println(treeSet.toString());
            System.out.println("---------删除1------------");
            treeSet.remove(new Person2("李四",19));
            System.out.println("元素个数"+treeSet.size());
            System.out.println(treeSet.toString());
        }
    }
    //输出:
    元素个数4
    [张三 18, 李四 19, 王五 20, 赵六 16]
    ---------删除1------------
    元素个数3
    [张三 18, 王五 20, 赵六 16]
    
通过TreeSet实现字符串按长度进行排序
//实现从小到大
public class Demo08 {
    public static void main(String[] args) {
        TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                int n1=o1.length()-o2.length();
                int n2=o1.compareTo(o2);
                //先看长度是否相同,如果长度相同在通过compareTo比较
                return n1==0?n2:n1;
            }
        });
        treeSet.add("helloword");
        treeSet.add("pingguo");
        treeSet.add("zhansan");
        treeSet.add("lisi");
        treeSet.add("cat");
        treeSet.add("dog");
        System.out.println(treeSet.toString());

    }
}
//输出:
[cat, dog, lisi, pingguo, zhansan, helloword]
posted @   史小鹏  阅读(73)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示