专栏首页KEN DO EVERTHING「 深入浅出 」集合Set

「 深入浅出 」集合Set

Set继承自Collection接口,不能包含有重复元素。本篇文章主要讲Set中三个比较重要的实现类:HashSet、TreeSet。

Set

Set是一个存储无序且不重复元素的集合。 在使用Set集合的时候,应该注意两点

  • 为Set集合里的元素的实现类重写equals()和hashCode()方法()
  • 若传入重复的元素,重复元素会被忽略(可以用于做集合的去重)

扩展 判断两个元素相等的标准:两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

HashSet

HashSet是Set接口的典型实现,是哈希表结构,主要利用HashMap的key来存储元素,计算插入元素的hashCode来获取元素在集合中的位置,因此具有很好的存取和查找性能。

主要特点 1.不允许出现重复元素 2.存储的元素是无序的 3.不是同步的,如果多个线程同时访问一个HashSet,则必须通过代码来保证其同步。 4.集合元素值可以是null。

HashSet基本操作
public class HashSetTest {
        public static void main(String[] agrs){
            //创建HashSet集合:
            Set<String> hashSet = new HashSet<String>();
            System.out.println("HashSet初始容量大小:"+hashSet.size());

            //元素添加:
            hashSet.add("my");
            hashSet.add("name");
            hashSet.add("is");
            hashSet.add("ken");
            hashSet.add("hello");
            hashSet.add("everyone");
            System.out.println("HashSet容量大小:"+hashSet.size());

            //迭代器遍历:
            Iterator<String> iterator = hashSet.iterator();
            while (iterator.hasNext()){
                String str = iterator.next();
                System.out.print(str+" ");
            }
            System.out.print("\n");
            //元素删除:
            hashSet.remove("ken");
            System.out.println("HashSet元素大小:" + hashSet.size());
            hashSet.clear();
            System.out.println("HashSet元素大小:" + hashSet.size());

            //集合判断:
            boolean isEmpty = hashSet.isEmpty();
            System.out.println("HashSet是否为空:" + isEmpty);
            boolean isContains = hashSet.contains("hello");
            System.out.println("HashSet是否为空:" + isContains);
        }
}
//out:
HashSet初始容量大小:0
HashSet容量大小:6
ken everyone name is hello my 
HashSet元素大小:5
HashSet元素大小:0
HashSet是否为空:true
HashSet是否为空:false

细节注意事项可看以下例子:

//重写类A的equals方法总是返回true,但没有重写其hashCode()方法。
//不能保证当前对象是HashSet中的唯一对象
class A {
    @Override
    public boolean equals(Object obj) {
        return true;
    }
}

//重写类B的hashCode()方法总是返回1,但没有重写其equals()方法。
//不能保证当前对象是HashSet中的唯一对象
class B {
    @Override
    public int hashCode() {
        return 1;
    }
}

//重写重写类C的hashCode()方法总是返回2,且有重写其equals()方法
class C {
    @Override
    public int hashCode() {
        return 2;
    }

    @Override
    public boolean equals(Object obj) {
        return true;
    }
}

public class HashSetTest {
    public static void main(String[] args) {
        HashSet books = new HashSet();
        //分别向books集合中添加两个A对象,两个B对象,两个C对象
        books.add(new A());
        books.add(new A());

        books.add(new B());
        books.add(new B());

        books.add(new C());
        books.add(new C());
        System.out.println(books);
    }
}
//out
[B@1, B@1, C@2, A@3bc257, A@785d65]

可以看出,只有当同时重写了equals方法和hashCode方法时,才能按照自己的意图判断对象是否相等,否则均判定为不相等,可加入Set集合中。

TreeSet

与HashSet集合类似,TreeSet也是基于Map来实现,其底层结构为红黑树(特殊的二叉查找树) 与HashSet不同的是,TreeSet具有排序功能,分为自然排序(123456)和自定义排序两类,默认是自然排序

具有如下特点:

  • 对插入的元素进行排序,是一个有序的集合(主要与HashSet的区别)
  • 底层使用红黑树结构,而不是哈希表结构
  • 允许插入Null值
  • 不允许插入重复元素
  • 线程不安全
TreeSet基本操作
public class TreeSetTest {
     public static void main(String[] agrs){
        TreeSet<String> treeSet = new TreeSet<String>();
        System.out.println("TreeSet初始化容量大小:"+treeSet.size());

        //元素添加:
        treeSet.add("my");
        treeSet.add("name");
        treeSet.add("ken");
        treeSet.add("hello");
        treeSet.add("world");
        treeSet.add("1");
        treeSet.add("2");
        treeSet.add("3");
        System.out.println("TreeSet容量大小:" + treeSet.size());
        System.out.println("TreeSet元素顺序为:" + treeSet.toString());


        System.out.println("遍历元素升序:");
        //迭代器遍历:升序
        Iterator<String> iteratorAesc = treeSet.iterator();
        while(iteratorAesc.hasNext()){
            String str = iteratorAesc.next();
            System.out.print(str + " ");
        }
        System.out.println("\n");
        System.out.println("遍历元素降序:");
        //迭代器遍历:降序
        Iterator<String> iteratorDesc = treeSet.descendingIterator();
        while(iteratorDesc.hasNext()){
            String str = iteratorDesc.next();
            System.out.print(str + " ");
        }
        System.out.println("\n");
        //元素获取:实现NavigableSet接口
        String firstEle = treeSet.first();//获取TreeSet头节点:
        System.out.println("TreeSet头节点为:" + firstEle);

        // 获取指定元素之前的所有元素集合:(不包含指定元素)
        SortedSet<String> headSet = treeSet.headSet("ken");
        System.out.println("ken节点之前的元素为:"+headSet.toString());

        //获取给定元素之间的集合:(包含头,不包含尾)
        SortedSet subSet = treeSet.subSet("1","hello");
        System.out.println("1--hello之间节点元素为:"+subSet.toString());

        //集合判断:
        boolean isEmpty = treeSet.isEmpty();
        System.out.println("TreeSet是否为空:"+isEmpty);
        boolean isContain = treeSet.contains("who");
        System.out.println("TreeSet是否包含who元素:"+isContain);

        //元素删除:
        boolean kenRemove = treeSet.remove("ken");
        System.out.println("ken元素是否被删除"+kenRemove);

        //集合中不存在的元素,删除返回false
        boolean whoRemove = treeSet.remove("who");
        System.out.println("who元素是否被删除"+whoRemove);

        //删除并返回第一个元素:如果set集合不存在元素,则返回null
        String pollFirst = treeSet.pollFirst();
        System.out.println("删除的第一个元素:"+pollFirst);

        //删除并返回最后一个元素:如果set集合不存在元素,则返回null
        String pollLast = treeSet.pollLast();
        System.out.println("删除的最后一个元素:"+pollLast);

        treeSet.clear();//清空集合
    }
}
//out:
TreeSet初始化容量大小:0
TreeSet容量大小:8
TreeSet元素顺序为:[1, 2, 3, hello, ken, my, name, world]
遍历元素升序:
1 2 3 hello ken my name world 

遍历元素降序:
world name my ken hello 3 2 1 

TreeSet头节点为:1
ken节点之前的元素为:[1, 2, 3, hello]
1--hello之间节点元素为:[1, 2, 3]
TreeSet是否为空:false
TreeSet是否包含who元素:false
jiaboyan元素是否被删除false
who元素是否被删除false
删除的第一个元素:1
删除的最后一个元素:world
TreeSet元素排序

我们讲到了TreeSet是一个有序集合,可以对集合元素排序,其中分为自然排序和自定义排序

自然排序(正序与反序)
public class TreeSetTest {
   public static void main(String[] agrs){
        TreeSet<String> treeSetString = new TreeSet<String>();
        treeSetString.add("a");
        treeSetString.add("z");
        treeSetString.add("d");
        treeSetString.add("b");
        System.out.println("字母正序:" + treeSetString.toString());
        System.out.println("字母反序:" + treeSetString.descendingSet().toString());

        TreeSet<Integer> treeSetInteger = new TreeSet<Integer>();
        treeSetInteger.add(1);
        treeSetInteger.add(24);
        treeSetInteger.add(23);
        treeSetInteger.add(6);
        System.out.println("数字正序:" + treeSetInteger.toString());
        System.out.println("数字反序:" + treeSetInteger.descendingSet().toString());
    }
}
//out
字母顺序:[a, b, d, z]
数字顺序:[1, 6, 23, 24]
自定义排序

当使用的是自己定义的类时,就需要做一些特殊处理,否则会报错Exception in thread "main" java.lang.ClassCastException,有两种实现方式 1.实现Comparable接口

public class Person implements Comparable<Person>{
        private String name;
        private Integer age;
        public Person(){}
        public Person(String name,Integer age){
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
        @Override
        public int compareTo(Person person) {
            //如果name长度一样,则比较年龄的大小
            //需要考虑相等的情况,否则相等的情况只显示先加入集合的元素
            if(this.age.equals(person.age)){
                return 1;
            }
            return this.age - person.age;
        }
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }

    public static void main(String[] agrs){
        TreeSet<Person> treeSet = new TreeSet<Person>();

        //排序对象:
        Person p1 = new Person("ken",18);
        Person p2 = new Person("xiaoming",15);
        Person p3 = new Person("laowang",15);
        Person p4 = new Person("zhangsan",25);

        //添加到集合:
        treeSet.add(p1);
        treeSet.add(p2);
        treeSet.add(p3);
        treeSet.add(p4);
        System.out.println("TreeSet集合顺序为:"+treeSet);
    }

}
//out:
TreeSet集合顺序为:[Person{name='xiaoming', age=15}, Person{name='laowang', age=15}, 
Person{name='ken', age=18}, Person{name='zhangsan', age=25}]
2.实现Comparetor接口,并重写compare方法
//自定义Person类的比较器:
public class PersonComparator implements Comparator<Person> {
    @Override
    public int compare(Person p1, Person p2) {
        //比较名字长度,从大到小排序
        //需要考虑相等的情况,否则相等的情况只显示先加入集合的元素
        if(p2.getName().length() == p1.getName().length()){
            return  1;
        }
        return p2.getName().length() - p1.getName().length();
    }
}

测试程序

 public static void main(String[] agrs){
        TreeSet<Person> treeSet = new TreeSet<Person>(new PersonComparator());

        //排序对象:
        Person p1 = new Person("ken",18);
        Person p2 = new Person("xiaoming",15);
        Person p3 = new Person("laowang",15);
        Person p4 = new Person("zhangsan",25);

        //添加到集合:
        treeSet.add(p1);
        treeSet.add(p2);
        treeSet.add(p3);
        treeSet.add(p4);
        System.out.println("TreeSet集合顺序为:"+treeSet);
    }
//out
TreeSet集合顺序为:[Person{name='xiaoming', age=15}, Person{name='zhangsan', age=25}, 
Person{name='laowang', age=15}, Person{name='ken', age=18}]

后续文章将对java集合中的具体实现类进行深入了解。有兴趣的话可以观看后续内容,进一步了解java集合内容。

本文分享自微信公众号 - java从心(javaFollowHeart),作者:a丶ken

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-12-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 深入学习MySQL 02 日志系统:bin log,redo log,undo log

    上一篇文章中,我们了解了一条查询语句的执行过程,按理说这篇应该讲一条更新语句的执行过程,但这个过程比较复杂,涉及到了好几个日志与事物,所以先梳理一下3个重要的日...

    KEN DO EVERTHING
  • 快速入门Vue

    刚进公司做的第一个项目,刚好前端人手不足,需要我们后端同时兼顾前后端的工作,采用的iview UI框架,基于vue.js。

    KEN DO EVERTHING
  • 每日一题(10)

    解析: 你有可能一下子就说出了 60 因为能整除 60 * 1000 的只能是 1 * 60 * 1000 ,2 * 60 * 1000 ….一直到 60 *...

    KEN DO EVERTHING
  • HTML5和原生APP优缺点大对比,世界究竟是谁的?

    近两年来,HTML5的发展势头确实很猛,甚至都出现了很多言论说:HTML5终将取代原生app,app终将消亡的言论。其实对此来说:小编不能 够太认同。HTML5...

    非著名程序员
  • 为 a.out 举行一个特殊的告别仪式

    在 “Linux 发布 5.1, Linux Lab 同步支持” 一文中,首次得知了 Linux 移除 a.out 格式的消息,这个消息着实令人感叹,因为 a....

    Linux阅码场
  • 基于Java的开源 Carrot2 搜索结果聚合聚类引擎 2.0发布了

    基于Java的开源 Carrot2 搜索结果聚合聚类引擎 2.0发布了. Carrot2 可以自动的把自然的搜索结果归类(聚合聚类)到相应的语义类别中,这个功能...

    田春峰-JCJC错别字检测
  • Density利用AI和传感器实时统计室内人数

    在全球定位无处不在的世界里,跟踪一个人群的运动从未如此简单。但跟踪到办公室就不那么容易了,角落,走廊,门口和会议室使室内人员追踪成为挑战,但这并没有阻碍企业家A...

    AiTechYun
  • MySQL SQL语句是如果被执行的?(1)原

    如上一个SQL语句,发送到MySQL服务器之后,会做什么,如何识别上边语句并返回结果?下面我们来详细说明这个过程。

    兜兜毛毛
  • 【DB笔试面试526】在Oracle中, 什么是专用服务器和共享服务器?

    在连接到Oracle数据库的时候,可以有两种连接模式,一种叫做专用服务器连接(Dedicated Server),另外一种叫做共享服务器连接(Shared Se...

    小麦苗DBA宝典
  • 用shell脚本实现转录组测序中 解压-比对-计算基因表达量 一体化

    HUBU生信

扫码关注云+社区

领取腾讯云代金券