Java集合框架之Set接口、实现类、排序

JDK1.8-API在线中文版

1. Set接口与实现类

特点:
1)无序、无下标、元素不可重复(当插入新元素时,如果新元素与已有元素进行equals比较,结果为true时,则拒绝新元素插入)
2)set接口并没有提供自己独有的方法,均是继承Collection的方法

Set 注重独一无二的性质,该体系集合用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复。对象的相等性本质是对象 hashCode 值(java 是依据对象的内存地址计算出的此序号)判断的,如果想要让两个不同的对象视为相等的,就必须覆盖 Object 的 hashCode 方法和 equals 方法。

元素不重复的基本逻辑判断示意图:

重写hashCode和equals方法

包装类型,重写hashCode就很简单:

class Student{
    String name;
    Integer age;
    String sex;
    Double score;
    // ...
    @Override
    public int hashCode() {
        return (this.name.hashCode() + this.sex.hashCode() + this.age.hashCode() + this.score.hashCode());
    }
    
}

基本类型,重写hashCode详解:【Java】如何重写hashCode()和equals()方法

Set接口的实现类:HashSet、LinkedHashSet、TreeSet

① HashSet类(基于HashCode-无序)

特点:

  • 基于HashCode实现元素不重复 - 无序
  • 当存入元素的哈希码相同时,会调用equals确认,结果为true,则拒绝后者加入
  • 无参构建初始容量为16(负载因子0.75,即+75%容量扩容)
  • 底层使用的HashMap类,即将所有需要存储的值,通过HashMap去重存入
  • 先判断hashCode是否相同,再==比较地址是否相同,再equals内容是否相同
import java.util.HashSet;
import java.util.Set;
public class TestHashSet3 {
      public static void main(String[] args) {
            Student s1 = new Student("tom", 20, "male", 99.0); //  0x0000 1111
            Student s2 = new Student("jack", 23, "male", 88.0);
            Student s3 = new Student("mark", 22, "male", 95.0);
            Student s4 = new Student("anna", 21, "female", 93.0);
            Student s5 = new Student("tom", 20, "male", 99.0); //  0x0000 2222
            
            Set<Student> students = new HashSet<Student>(); // 快速导包:Ctrl+Shift+O
            students.add(s1); // 0x0000 1111
            students.add(s2);
            students.add(s3);
            students.add(s4);
            students.add(s1); // 0x0000 1111, add失败,去掉了重复:equals--->Object类提供的(this==obj)判断
            students.add(s5); // 0x0000 2222(内容相同,地址不同)也需要去重
            
            for (Student student : students) {
                  System.out.println(student.toString());
            }
            
            /*
             * 注意:HashSet没有必要在每次插入一个新值时对数据都去一一比较
             * 注意:HashSet调用equals方法进行比较,是具有前提的(两个对象的哈希码相同)
             */
            
            System.out.println(s1.equals(s5));
      }
}
class Student{
      String name;
      Integer age;
      String sex;
      Double score;
      public Student() {}
      public Student(String name, Integer age, String sex, Double  score) {
            super();
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.score = score;
      }
      @Override
      public String toString() {
            return "Student [name=" + name + ", age=" + age + ", sex="  + sex + ", score=" + score + "]";
      }
      @Override
      public boolean equals(Object obj) {
            System.out.println("Student's equals() method  executed...");
            
            if (this == obj) { return true; }
            if (null == obj) { return false; }
            if (this.getClass() != obj.getClass()) { return false; }
            Student s = (Student)obj;
            
            if (this.toString().equals(s.toString())) {
                  return true;
            }
            
            return false;
      }
      @Override
      public int hashCode() {
            return (this.name.hashCode() + this.sex.hashCode() +  this.age.hashCode() + this.score.hashCode());
      }
}

② LinkedHashSet类(记录插入顺序)

特点:

  • 继承自HashSet,又基于LinkedHashMap来实现的
  • 底层使用LinkedHashMap(链表结构)存储,节点形式独立存储数据,并可以指向下一个节点,通过顺序访问节点,可保留元素的插入顺序 - 插入顺序
  • 所有方法与HashSet相同,用法也一模一样
import java.util.LinkedHashSet;
public class TestLinkedHashSet {
      public static void main(String[] args) {
            // 底层使用LinkedHashMap(链表结构)存储,节点形式完成单独数据的保存
            // 并可以指向下一个节点,通过顺序访问节点,可保留元素的插入顺序
            LinkedHashSet<String> set = new LinkedHashSet<String>();
            set.add("aa");
            set.add("bb");
            set.add("cc");
            set.add("dd");
            set.add("ee");
            
            for (String s : set) {
                  System.out.print(s + " ");
            }
            System.out.println();
            
            LinkedHashSet<Integer> lhs = new LinkedHashSet<Integer>();
            lhs.add(11);
            lhs.add(99);
            lhs.add(66);
            lhs.add(33);
            lhs.add(55);
            lhs.add(44);
            
            for (Integer i : lhs) {
                  System.out.print(i + " ");
            }
            
      }
}

③ TreeSet类(二叉树-自动排序)

特点:

  • 基于排列顺序实现元素不重复 - 自动排序
  • 实现了SortedSet接口,对所有插入集合的元素自动排序
  • 元素对象的类型必须实现Comparable接口,指定排序规则(Integer/String类默认实现),通过重写CompareTo方法才能使用,以确定是否为重复元素
import java.util.TreeSet;
public class TestTreeSet2 {
      public static void main(String[] args) {
            TreeSet<Student> stus = new TreeSet<Student>();
            
            stus.add(new Student("tom", 20, "male", 80.0));
            stus.add(new Student("jak", 20, "male", 90.0));
            stus.add(new Student("vae", 20, "male", 100.0));
            stus.add(new Student("com", 20, "male", 60.0));
            
            for (Student student : stus) {
                  System.out.println(student.toString());
            }
      }
}
class Student implements Comparable<Student>{
      String name;
      Integer age;
      String sex;
      Double score;
      public Student() {
            super();
      }
      public Student(String name, Integer age, String sex, Double  score) {
            super();
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.score = score;
      }
      @Override
      public int compareTo(Student o) {
            //主要排序列
            if (this.score < o.score) {
                  return -1;
            } else if (this.score > o.score){
                  return 1;
            } else {
                  //次要排序列(排序的属性中可能是个多属性的对象)
                  if (this.age < o.age) {
                        return -1;
                  } else if (this.age > o.age) {
                        return 1;
                  }
            }
            
            return 0;
      }
      @Override
      public String toString() {
            return "Student [name=" + name + ", age=" + age + ", sex="  + sex + ", score=" + score + "]";
      }
}

2. HashSet排序的两种方法

1)遍历加入到List中使用Collections.sort(list)排序;
2)使用TreeSet的构造创建一个TreeSet对象实现自动排序;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.TreeSet;
public class TestHashSetSort {
      public static void main(String[] args) {
            /* 对HashSet排序的两种办法验证 */
            HashSet<Integer> numSet = new HashSet<Integer>();
            numSet.add(55);
            numSet.add(11);
            numSet.add(66);
            numSet.add(88);
            numSet.add(33);
            System.out.println(numSet); // [33, 66, 55, 88, 11]
            System.out.println("------------------------");
            sortByTreeSet(numSet); // [11, 33, 55, 66, 88]
            System.out.println("------------------------");
            sortByList(numSet); // [11, 33, 55, 66, 88]
            
      }
      // 装进TreeSet里,自动排序
      public static void sortByTreeSet(HashSet<Integer> set) {
            TreeSet<Integer> numTree = new TreeSet<Integer>(set);
            System.out.println(numTree);
      }
      
      // 装进ArrayList里,通过sort排序
      public static void sortByList(HashSet<Integer> set) {
            List<Integer> numList = new ArrayList<Integer>();
            for (Integer i : set) {
                  numList.add(i);
            }
            Collections.sort(numList);
            System.out.println(numList);
      }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容