Set接口

概述

一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对
e1e2,并且最多包含一个 null 元素

特点

  1. Set接口是无序的
  2. Set 是继承于Collection的接口。它是一个不允许有重复元素的集合。
  3. Set可以存储null值,但是null不能重复
  4. Set的实现类都是基于Map来实现的(HashSet是通过HashMap实现的,TreeSet是通过TreeMap实现的)。

代码示例

Set<String> set = new HashSet<>();
set.add("ab");
set.add("ac");
set.add("ba");
set.add("bc");System.out.println(set);运行结果: [ab, bc, ac, ba]

HashSet

概述

此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。

特点

  1. 底层数据结构是 哈希表,HashSet的本质是一个"没有重复元素"的集合,它是通过HashMap实现的。HashSet中含有一个"HashMap类型的成员变量"map,在HashSet中操作函数,实际上都是通过map实现的。

  2. 哈希表保证唯一 依赖hashcode和equals方法

    ​ 原理:
    ​ 首先判断hashCode是否相同
    ​ 不相同
    ​ 就存储到集合中
    ​ 相同
    ​ 比较equals方法是否相同
    ​ 相同 就不存储
    ​ 不相同就以链表的方式存储到集合中

  3. 哈希表导致元素存储无序主要因为系统通过哈希算法计算出来的索引和对象本身的hashCode本身有关,所以这个整数值是无序的,从而存储到集合中自然就是无序的

底层源码分析示例

HashSet<String> hs = new HashSet<>();
set.add("ab");
set.add("ac");
set.add("ba");
set.add("bc");class HashSet {private transient HashMap<E,Object> map;private static final Object PRESENT = new Object(); public HashSet() {map = new HashMap<>();}public boolean add(E e) {return map.put(e, PRESENT)==null;}
}class HashMap {public V put(K key, V value) {                  return putVal(hash(key), key, value, false, true);}// 哈希算法和存储对象本身有关static final int hash(Object key) {int h;// 哈希值和对象的hashCode有关return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;// 判断元素是否唯一取决于哈希值和equals方法if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;}
}class String {private int hash; // Default to 0// 字符串hashCode方法的重写规则public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {char val[] = value;// abfor (int i = 0; i < value.length; i++) {h = 31 * h + val[i];  // 31 * 0 + 97h = 31 * 97 + 98}hash = h;}return h;}// 字符串equals方法的重写规则public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String anotherString = (String)anObject;int n = value.length;if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) {if (v1[i] != v2[i])return false;i++;}return true;}}return false;}
}

HashSet存储自定义对象去除重复元素需要重写 hashCode和equals方法

public class HashSetDemo02 {public static void main(String[] args) {HashSet<Student> hs = new HashSet<>();hs.add(new Student("zhangsan", 18));hs.add(new Student("lisi", 20));hs.add(new Student("zhangsan", 18));hs.add(new Student("wangwu", 30));for (Student s : hs) {System.out.println(s);}}
}class Student {private String name;private int age;public Student() {super();}public Student(String name, int age) {super();this.name = name;this.age = 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;}@Overridepublic String toString() {return "Student [name=" + name + ", age=" + age + "]";}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + age;result = prime * result + ((name == null) ? 0 : name.hashCode());return result;}@Overridepublic 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;}}
程序运行结果
Student [name=lisi, age=20]
Student [name=zhangsan, age=18]
Student [name=wangwu, age=30]

TreeSet

概述

基于 TreeMapNavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。

特点

  1. TreeSet 是一个有序的并且可排序的集合,它继承于AbstractSet抽象类,实现了NavigableSet, Cloneable, java.io.Serializable接口。

  2. TreeSet是基于TreeMap实现的。TreeSet中的元素支持2种排序方式:自然排序 或者 根据创建TreeSet 时提供的 Comparator 进行排序。这取决于使用的构造方法。

  3. TreeSet是非同步的,线程不安全的,效率高。

  4. 二叉树保证元素唯一

    ​ 【

    ​ 第一个元素进来作为根节点存储

    ​ 后面元素进来和根节点比较

    ​ 大了,放在元素的右边

    ​ 小了,放在元素的左边

    ​ 相等,设置原值

  5. 二叉树保证元素可排序 --> 利用二叉树中序遍历取元素的特点。

底层源码分析示例

// 使用TreeSet无参构造方法实现自然排序
TreeSet<Integer> ts = new TreeSet<>();
// 使用TreeSet带Comparator接口参数的构造方法实现比较其排序
// TreeSet<Student> ts = new TreeSet<>(new StudentComparator());ts.add(40);
ts.add(38);
ts.add(43);
ts.add(42);
ts.add(37);
ts.add(44);
ts.add(39);
ts.add(38);
ts.add(44);class TreeSet {private transient NavigableMap<E,Object> m; private static final Object PRESENT = new Object(); public TreeSet() {this(new TreeMap<E,Object>());}// Comparator<? super E> comparator = new StudentComparator();public TreeSet(Comparator<? super E> comparator) {this(new TreeMap<>(comparator));}// NavigableMap<E,Object> m = new TreeMap<>(comparator);TreeSet(NavigableMap<E,Object> m) {this.m = m;}// NavigableMap<E,Object> m = new TreeMap<E,Object>();TreeSet(NavigableMap<E,Object> m) {this.m = m;}public boolean add(E e) {return m.put(e, PRESENT)==null;}}class TreeMap implements NavigableMap<K,V> {private transient Entry<K,V> root;private final Comparator<? super K> comparator; public TreeMap() {comparator = null;}// Comparator<? super K> comparator = new StudentComparator();public TreeMap(Comparator<? super K> comparator) {this.comparator = comparator;}public V put(K key, V value) {Entry<K,V> t = root; // 如果第一个元素进来,作为根节点存储if (t == null) {compare(key, key); // type (and possibly null) check// 创建根节点root = new Entry<>(key, value, null);size = 1;modCount++;return null;}int cmp;// 创建父节点Entry<K,V> parent;// split comparator and comparable pathsComparator<? super K> cpr = comparator; // cpr = nullif (cpr != null) {// 比较器排序do {parent = t;cmp = cpr.compare(key, t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;elsereturn t.setValue(value);} while (t != null);}else {// 自然排序if (key == null)throw new NullPointerException();@SuppressWarnings("unchecked")Comparable<? super K> k = (Comparable<? super K>) key; 38 Integerdo {// 根节点作为父节点parent = t;cmp = 38 k.compareTo(t.key 40); -1if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;elsereturn t.setValue(value);} while (t != null);}Entry<K,V> e = new Entry<>(key, value, parent);if (cmp < 0)parent.left = e;elseparent.right = e;fixAfterInsertion(e);size++;modCount++;return null;}
}class Integer implements Comparable<Integer>{public int compareTo(Integer anotherInteger) {return compare(this.value, anotherInteger.value);}public static int compare(int x, int y) {return (x < y) ? -1 : ((x == y) ? 0 : 1);}
}interface Comparable {public int compareTo(T o);
}interface Comparator {int compare(T o1, T o2);
}

排序标准模板写法代码实例

/** 2.键盘录入5个学生信息(姓名,年龄,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台*    注:总分相同等情况下按照语文成绩排序,其次是数学成绩、英语成绩、年龄、姓名*/
public class TreeSetDemo04 {public static void main(String[] args) {// 匿名内部类实现Comparator接口方式创建TreeSet对象TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {@Overridepublic int compare(Student s1, Student s2) {// 排序标准模板写法Collator cmp = Collator.getInstance(java.util.Locale.CHINA);double num1 = s1.getTotalScore() - s2.getTotalScore();double num2 = (num1 == 0) ? s1.getChineseScore() - s2.getChineseScore() :num1;double num3 = (num2 == 0) ? s1.getMathScore() - s2.getMathScore() : num2;double num4 = (num3 == 0) ? s1.getEnglishScore() - s2.getEnglishScore() : num3;double num5 = (num4 == 0) ? s1.getAge() - s2.getAge() : num4;double num6 = (num5 == 0) ? cmp.compare(s1.getName(), s2.getName()): num5;return (num6 < 0) ? -1 : ((num6 == 0) ? 0 : 1);}});for (int i = 0; i < 30; i++) {String name = RandomValue.getChineseName();int age = RandomValue.getRandomNum(18, 20);double chineseScore = RandomValue.getRandomNum(88, 90);double mathScore = RandomValue.getRandomNum(90, 91);double englishScore = RandomValue.getRandomNum(88, 89);Student s = new Student(name, age, chineseScore, mathScore, englishScore);ts.add(s);}for (Student s : ts) {System.out.println(s);}}
}class Student {private String name;private int age;private double chineseScore;private double mathScore;private double englishScore;此处省略构造方法... public double getTotalScore() {return this.chineseScore + this.mathScore + this.englishScore;}此处省略get/set方法...}public class RandomValue {public static String base = "abcdefghijklmnopqrstuvwxyz0123456789";private static String firstName = "赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏水窦章云苏潘葛奚范彭郎鲁韦昌马苗凤花方俞任袁柳酆鲍史唐费廉岑薛雷贺倪汤滕殷罗毕郝邬安常乐于时傅皮卞齐康伍余元卜顾孟平黄和穆萧尹姚邵湛汪祁毛禹狄米贝明臧计伏成戴谈宋茅庞熊纪舒屈项祝董梁杜阮蓝闵席季麻强贾路娄危江童颜郭梅盛林刁钟徐邱骆高夏蔡田樊胡凌霍虞万支柯咎管卢莫经房裘缪干解应宗宣丁贲邓郁单杭洪包诸左石崔吉钮龚程嵇邢滑裴陆荣翁荀羊於惠甄魏加封芮羿储靳汲邴糜松井段富巫乌焦巴弓牧隗山谷车侯宓蓬全郗班仰秋仲伊宫宁仇栾暴甘钭厉戎祖武符刘姜詹束龙叶幸司韶郜黎蓟薄印宿白怀蒲台从鄂索咸籍赖卓蔺屠蒙池乔阴郁胥能苍双闻莘党翟谭贡劳逄姬申扶堵冉宰郦雍却璩桑桂濮牛寿通边扈燕冀郏浦尚农温别庄晏柴瞿阎充慕连茹习宦艾鱼容向古易慎戈廖庚终暨居衡步都耿满弘匡国文寇广禄阙东殴殳沃利蔚越夔隆师巩厍聂晁勾敖融冷訾辛阚那简饶空曾毋沙乜养鞠须丰巢关蒯相查后江红游竺权逯盖益桓公万俟司马上官欧阳夏侯诸葛闻人东方赫连皇甫尉迟公羊澹台公冶宗政濮阳淳于仲孙太叔申屠公孙乐正轩辕令狐钟离闾丘长孙慕容鲜于宇文司徒司空亓官司寇仉督子车颛孙端木巫马公西漆雕乐正壤驷公良拓拔夹谷宰父谷粱晋楚阎法汝鄢涂钦段干百里东郭南门呼延归海羊舌微生岳帅缑亢况后有琴梁丘左丘东门西门商牟佘佴伯赏南宫墨哈谯笪年爱阳佟第五言福百家姓续";private static String girl = "秀娟英华慧巧美娜静淑惠珠翠雅芝玉萍红娥玲芬芳燕彩春菊兰凤洁梅琳素云莲真环雪荣爱妹霞香月莺媛艳瑞凡佳嘉琼勤珍贞莉桂娣叶璧璐娅琦晶妍茜秋珊莎锦黛青倩婷姣婉娴瑾颖露瑶怡婵雁蓓纨仪荷丹蓉眉君琴蕊薇菁梦岚苑婕馨瑗琰韵融园艺咏卿聪澜纯毓悦昭冰爽琬茗羽希宁欣飘育滢馥筠柔竹霭凝晓欢霄枫芸菲寒伊亚宜可姬舒影荔枝思丽 ";private static String boy = "伟刚勇毅俊峰强军平保东文辉力明永健世广志义兴良海山仁波宁贵福生龙元全国胜学祥才发武新利清飞彬富顺信子杰涛昌成康星光天达安岩中茂进林有坚和彪博诚先敬震振壮会思群豪心邦承乐绍功松善厚庆磊民友裕河哲江超浩亮政谦亨奇固之轮翰朗伯宏言若鸣朋斌梁栋维启克伦翔旭鹏泽晨辰士以建家致树炎德行时泰盛雄琛钧冠策腾楠榕风航弘";/*** 随机返回任意范围的整数*/public static int getRandomNum(int start, int end) {return (int) (Math.random() * (end - start + 1) + start);}/*** 随机返回中文姓名*/public static String getChineseName() {String name_sex = "";int index = getRandomNum(0, firstName.length() - 1);String first = firstName.substring(index, index + 1);int sex = getRandomNum(0, 1);String str = boy;int length = boy.length();if (sex == 0) {str = girl;length = girl.length();name_sex = "女";} else {name_sex = "男";}index = getRandomNum(0, length - 1);String second = str.substring(index, index + 1);int hasThird = getRandomNum(0, 1);String third = "";if (hasThird == 1) {index = getRandomNum(0, length - 1);third = str.substring(index, index + 1);}return first + second + third;}}
  1. 注意中文排序可以使用 Collator类处理
  2. 建议使用三目运算符进行排序
  3. 针对返回值是double类型的,可以考虑使用三目转换成int类型的结果

LinkeHashSet

概述

List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现
List接口外,LinkedList类还为在列表的开头及结尾getremoveinsert` 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。

特点

  1. 底层数据结构是 链表和哈希表
  2. 链表保证元素有序
  3. 哈希表保证元素唯一

代码示例

LinkedHashSet<String> lhs = new LinkedHashSet<>();
lhs.add("中国");
lhs.add("美国");
lhs.add("美国");
lhs.add("德国");
System.out.println(lhs);程序运行结果:
[中国, 美国, 德国]

Set接口的一些主要集合总结相关推荐

  1. 5.0 java集合框架中的接口collection属于_JAVA集合框架 - osc_cyo2dovg的个人空间 - OSCHINA - 中文开源技术交流社区...

    一.为什么要使用集合 单个数据,可以用变量保存: 多个数据,可以用数组保存: 但是对于存储多个数据且数量不确定的情况,使用集合: 二.集合和数组的区别 (1)数组: 1.只能保存同一种类型的数据: 2 ...

  2. MongoDB学习笔记~为IMongoRepository接口添加分页取集合的方法

    回到目录 对于数据分页,我们已经见的太多了,几乎每个列表页面都要用到分页,这已经成了一种定理了,在进行大数据展示时,如果不去分页,而直接把数据加载到内存,这简直是不可以去相向的,呵呵,在很多ORM工具 ...

  3. java中单列集合的根接口是_java 单列集合总结

    Collection 接口 add() remove() contains() clear(); size(); 迭代器遍历(普通迭代器,不能再遍历过程中修改集合的长度) List接口 单列集合 有序 ...

  4. java list接口方法_java List集合接口的坑

    原标题:java List集合接口的坑 java推荐面向接口编程,我们一般写函数参数的时候总会选择接口的声明,然后根据接口的方法来进行操作.这里说说list的一个坑. 说起list的实现类,你一定能想 ...

  5. 2接口详解_java集合【2】——— Collection接口详解

    一.Collection接口简介 二.Collection源码分析 三.Collection的子类以及子类的实现 3.1 List extend Collection 3.2 Set extend C ...

  6. 【第17天】Java集合(四)---Sorted接口实现的TreeSet集合及单值类型集合总结

    1 TreeSet简介 2 基本用法与特点 3 制定单值比较规则 3.1 自然排序(compareTo(Object obj)) 3.2 定制排序(定义比较器类) 3.2.1 普通类内定义 3.2.2 ...

  7. java util包排序_实现java.util.Comparator接口,对对象集合进行多属性组合排序

    Commons - BeanUtils 提供了很多功能,其中一个很有用的是对对象集合进行排序,如Collections.sort(peoples, new BeanComparator("a ...

  8. java 集合 接口_Java集合之Collection接口

    1 - Java集合介绍 /* 1. 一方面, 面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象 的操作,就要对对象进行存储. 2. 另一方面,使用Array存储对象方面具有一些弊 端,而 ...

  9. (1)学习数组,集合,IEnumerable接口,引申学习迭代器

    发展:数组-->集合-->泛型 (1)数组 1. 数组数据结构是System.Array类的一个实例. 2. System.Array类的语法为 [SerializableAttribut ...

最新文章

  1. 2018/8/28-29 Some metaheuristics should be simplified
  2. Centos6.3修改源码遇到无法yum安装的问题
  3. DL:基于sklearn的加利福尼亚房价数据集实现GD算法
  4. 字节跳动 2019 ICME 双赛道冠军团队方案分享
  5. VTK:图表之NOVCAGraph
  6. beta分布_浅谈脑电的beta频段振荡
  7. 2018企业面试总汇(答案请自行搜罗) 新增19年阿里面题(反向拓展技术栈)
  8. (JAVA)List
  9. 排列组合(HDU-1521)
  10. java visitor_java 的visitor方式
  11. Raki的读paper小记:XLNet: Generalized Autoregressive Pretraining for Language Understanding
  12. Spring框架入门程序:获取Bean的三种方式
  13. 【总结】密码学详细学习
  14. 如何在html中在线编辑word文档,web版word在线编辑
  15. 移动硬盘无法读取分区表修复RAW格式修复
  16. 图算法图神经网络归纳总结
  17. Kubernetes之Service
  18. K8sPod资源基础管理操作
  19. 华为ensp NAT技术地址转换
  20. 图片文字翻译的软件有哪些?文字翻译软件推荐。

热门文章

  1. Money Pro for Mac 1.9.2 中文破解版下载 账单计划预算管理软件
  2. 黑盒测试技术和测试用例的设计方法
  3. 实施部署(锐捷云桌面篇)
  4. imx6dl系统每次启动MAC地址都不一样
  5. 猪年快乐之TensorFlow中实现word2vec及如何结构化TensorFlow模型
  6. 小说cmsPTCMS安装
  7. Linux批量替换带横杠日期
  8. 在超声波热量表、水表中MS1030、MS726、MCU MS616F512应用方案分享
  9. 《华为研发》 张利华
  10. 万年历软件各个接口功能的实现