Set集合

HashSet类

import java.util.HashSet;/*Set集合:元素唯一且元素无序(存储和取出顺序不一致)的集合HashSet类概述不保证 set 的迭代顺序特别是它不保证该顺序恒久不变。HashSet如何保证元素唯一性底层数据结构是哈希表(元素是链表的数组)哈希表依赖于哈希值存储添加功能底层依赖两个方法:int hashCode()boolean equals(Object obj)Set集合中的元素为什么不会重复?看添加add()方法的源码
*/
public class SetDemo1 {public static void main(String[] args) {//用Set的子类去实例化HashSet<String> arr = new HashSet<>();//添加元素到集合arr.add("hello");arr.add("world");arr.add("java");arr.add("bigdata");arr.add("hadoop");arr.add("hello");arr.add("hello");arr.add("java");arr.add("spark");arr.add("flink");arr.add("world");arr.add("hadoop");//增强for循环遍历for(String s : arr){System.out.println(s);}}
}//----------HashSet<>()中add()的源码分析-------public interface Set<E> extends Collection<E>{}public class HashSet<E> extends AbstractSet<E> implements Set<E>{private static final Object PRESENT = new Object();private transient HashMap<E,Object> map;public boolean add(E e) {  //E -- String  //e -- "hello"return map.put(e, PRESENT)==null;}
}public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>{public V put(K key, V value) {//key -- "hello"//value -- new Object()return putVal(hash(key), key, value, false, true);}//简单理解为调用元素类的hashCode()方法计算哈希值static final int hash(Object key) { //"hello"int h;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;//根据元素对象计算好的哈希值再进行一次与运算,计算出的是该元素存储在哈希表中的位置//如果该元素的位置是null,说明该位置没有元素,可以进行存储//就创建新的结点,存储元素//分析到这一步我们得出第一个结论,存储的位置与元素类中的hashCode()有关。if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {//如果该元素的位置不是null,说明这个位置上已经有元素了,可以确定是哈希值是一样的//但是呢,我们并不能确定两个值是不是一样的Node<K,V> e; K k;//先将存入元素的哈希值与该位置中元素的哈希值做比较//如果哈希值都不一样,继续走判断instanceof()//如果哈希值都一样,会调用元素的equals(k)方法进行比较//如果equals(k)方法进行比较结果是false,继续向下执行,最终会将元素插入到集合中或者不插入//如果equals(k)方法进行比较结果是true,表示元素的哈希值和内容都一样,表示元素重复了//就覆盖,从现象上来看,其实就是不赋值//说到这里我们已经知道了add()方法和hashCode()以及equals()方法有关//会不会去重取决于元素类型有没有重写hashCode()以及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;}
}
import java.util.Objects;public class Student2 {private String name;private int age;public Student2() {}public Student2(String name, int age) {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;}//重写equals()和hashCode()方法@Overridepublic String toString() {return "Student2{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student2 student2 = (Student2) o;return age == student2.age &&Objects.equals(name, student2.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}import java.util.HashSet;/*存储自定义对象并遍历
*/
public class HashSetDemo2 {public static void main(String[] args) {//创建集合对象HashSet<Student2> hashSet = new HashSet<>();//创建学生对象Student2 s1 = new Student2("xiaowang", 18);Student2 s2 = new Student2("xiaowang", 18);Student2 s3 = new Student2("xiaoli", 19);Student2 s4 = new Student2("xiaoliu", 20);hashSet.add(s1);hashSet.add(s2);hashSet.add(s3);hashSet.add(s4);for(Student2 s:hashSet){System.out.println(s);}//输出结果发现没有去重,这是为什么呢?//根据刚才add()的源码分析,如果没有在Student2类中重写equals()和hashCode()//比较的就是对象的地址值,而s1到s4的地址值都不一样,所以都添加。没有达到去重的目的。//想要去重,就需要在Student2类中重写equals()和hashCode()方法}
}

LinkedHashSet类

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.TreeSet;/*public class HashSet<E> implements Set<E>{}public class LinkedHashSet<E> extends HashSet<E>{}LinkedHashSet:底层数据结构是哈希表和双向链表哈希表保证了元素唯一链表保证了元素的有序(存储和取出顺序一致)
*/
public class LinkedHashSetDemo {public static void main(String[] args) {//创建LinkedHashSet集合对象LinkedHashSet<String> arr = new LinkedHashSet<>();//添加元素到集合arr.add("hello");arr.add("world");arr.add("java");arr.add("bigdata");arr.add("hadoop");arr.add("hello");arr.add("hello");arr.add("java");arr.add("spark");arr.add("flink");arr.add("world");arr.add("hadoop");for (String s : arr){System.out.println(s);}}
}

TreeSet类及其自然排序

import java.util.TreeSet;/*TreeSet:元素唯一,元素的顺序可以按照某种规则进行排序两种排序方式:自然排序 :无参构造--实现comparable接口比较器排序 :有参构造--实现Comparator接口A NavigableSet实现基于TreeMap 。的元件使用其有序natural ordering ,或由Comparator集合创建时提供,这取决于所使用的构造方法。要想知道如何去重以及排序,就去看源码。因为Integer类实现了comparable接口,所以它可以做自然排序*/public class TreeSetDemo1 {public static void main(String[] args) {//创建一个集合对象TreeSet<Integer> ts = new TreeSet<>();//添加元素到集合中ts.add(20);ts.add(18);ts.add(23);ts.add(24);ts.add(66);ts.add(12);ts.add(18);ts.add(20);ts.add(23);ts.add(2);//遍历for(Integer i : ts){System.out.println(i);}}
}
//---------------TreeSet集合的add()方法的源码-----------------------------------------------------
interface Collection {...
}interface Set extends Collection {...
}
---------------------------------------
class TreeSet implements Set {...private static final Object PRESENT = new Object();private transient NavigableMap<E,Object> m;public TreeSet() {this(new TreeMap<E,Object>());}public boolean add(E e) {return m.put(e, PRESENT)==null;}...
}
---------------------------------------
class TreeMap implements NavigableMap {...public V put(K key, V value) {Entry<K,V> t = root; // 先造根,TreeSet集合底层数据结构是红黑树(是一个自平衡的二叉树)if (t == null) {compare(key, key); // type (and possibly null) checkroot = 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; // 因为用的是TreeSet的无参构造方法,是自然排序,没有用到comparator比较器if (cpr != null) {                      // 所以此时的comparator = null,则程序执行else里面的代码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();Comparable<? super K> k = (Comparable<? super K>) key; // 此接口Comparable强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。。do {                                                   // 举例中我们使用的是包装类Intrger,而Integer类实现了Comparable接口。此例子是向上转型。parent = t;cmp = k.compareTo(t.key); // 类的 compareTo 方法被称为它的自然比较方法。if (cmp < 0)              // int compareTo(T o) 比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。 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;}...
}
---------------------------------------由上可知:真正的比较是依赖于元素的compareTo()方法,而这个方法compareTo()是定义在 Comparable接口里面的(抽象方法)。所以,你要想重写该方法,就必须是先实现 Comparable接口。这个接口表示的就是自然排序。
public class Student3 implements Comparable<Student3> {//为了解决这个问题,让Student3实现Comparable接口private String name;           //尖括号中放当前元素类型--Student3,并且重写Comparable接口中的抽象方法private int age;public Student3() {}public Student3(String name, int age) {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 "Student2{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Student3 o) {
//        return 0;--只插入第一个元素
//        return 1;--永远都可以插入,没有去重
//        return -1;--也不行,也没去重 //这里返回什么,其实应该根据我们的规则来排序//比如我想在去重的前提下,按照年龄进行排序
//        return this.age - o.age;//年龄一样,姓名不一定一样//主要条件(题目要求的条件)int i = this.age - o.age;//隐含条件(需要自己挖掘)int i2 = i == 0 ? this.name.compareTo(o.name) : i;return i2;}
}import java.util.TreeSet;/*TreeSet存储学生对象并遍历按照正常的写法,我们一运行就报错了java.lang.ClassCastException:类型转换异常由于我这里创建TreeSet对象调用的是无参构造方法,所以走的是自然排序而底层源码有一步向下转型Comparable<? super K> k = (Comparable<? super K>) key;报错了原因是我们Student3类没有实现Comparable接口,无法向下转型,所以报错了。*/
public class TreeSetDemo2 {public static void main(String[] args) {//创建集合对象TreeSet<Student3> ts = new TreeSet<>();//创建学生对象Student3 s1 = new Student3("周姐",24);Student3 s2 = new Student3("李元浩",25);Student3 s3 = new Student3("李湘赫",22);Student3 s4 = new Student3("汉子哥",26);Student3 s5 = new Student3("硬币哥",21);Student3 s6 = new Student3("乌兹",20);Student3 s7 = new Student3("李元浩",25);Student3 s8 = new Student3("厂长",25);//将学生对象插入到集合中ts.add(s1);ts.add(s2);ts.add(s3);ts.add(s4);ts.add(s5);ts.add(s6);ts.add(s7);ts.add(s8);for (Student3 s:ts){System.out.println(s);}}
}

集合--Set集合--HashSet类、LinkedHashSet类、TreeSet类及其自然排序相关推荐

  1. Java集合框架(二)—— HashSet、LinkedHashSet、TreeSet和EnumSet

    Set接口 前面已经简绍过Set集合,它类似于一个罐子,一旦把对象'丢进'Set集合,集合里多个对象之间没有明显的顺序.Set集合与Collection基本上完全一样,它没有提供任何额外的方法. Se ...

  2. Java 之HashSet、LinkedHashSet、TreeSet比较

    4.HashSet.LinkedHashSet.TreeSet比较 Set接口 Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false. Set判断两个对象相同不 ...

  3. HashSet、LinkedHashSet、TreeSet

    以下内容基于jdk1.7.0_79源码: 关于HashSet.LinkedHashSet.TreeSet Set接口的实现类,最大特点是不允许出现重复元素: HashSet:基于HashMap实现,一 ...

  4. set接口-存储及遍历、HashSet、LinkedHashSet、TreeSet

    文章目录 1 Set接口 1.1 概述 1.2 set接口中的方法 1.3 存储及遍历 1.3.1 存储字符串并遍历 1.3.2 存储自定义对象并遍历 2 HashSet 2.1 概述 2.2 构造方 ...

  5. 【JAVA基础】HashSet、LinkedHashSet、TreeSet使用区别

    [JAVA基础]HashSet.LinkedHashSet.TreeSet使用区别 HashSet:哈希表是通过使用称为散列法的机制来存储信息的,元素并没有以某种特定顺序来存放: LinkedHash ...

  6. Java集合(5)--Set接口及其实现类HashSet、LinkedHashSet和TreeSet

    文章目录 Set接口概述 HashSet实现类 LinkedHashSet实现类 TreeSet实现类 Set接口概述 1.Set接口是Collection的子接口,set接口没有定义额外的方法,使用 ...

  7. Set集合以及HashSet、LinkedHashSet、TreeSet等讲解

     Set •Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败. •Set 判断两个对象是否相同不是使用== 运算符,而是根据 equals 方法 •H ...

  8. Java15-day06【Set、HashSet、LinkedHashSet、TreeSet、Comparable、Comparator、泛型类、可变参数的使用】

    视频+资料(工程源码.笔记)[链接:https://pan.baidu.com/s/1MdFNUADVSFf-lVw3SJRvtg   提取码:zjxs] Java基础--学习笔记(零起点打开java ...

  9. Day48(List接口,ArrayList,LinkedList,Vector,Set接口,HashSet,LinkedHashSet,TreeSet,自然排序,定制排序)

    Collection子接口之一:List接口 List接口概述 鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组 List集合类中元素有序.且可重复,集合中的每个元素都有其对应的顺 ...

  10. 【JAVA】List转Set并按照List的顺序排序,HashSet、LinkedHashSet、TreeSet元素保存顺序List转换对比

    话不多说 直接代码测试效果: // 简化代码 直接 数组转list 就不用 写很多add了 哈哈哈 String[] array = {"f","a",&quo ...

最新文章

  1. 全文搜索引擎有哪些?_搜索引擎工作原理是什么?seo蜘蛛抓取会受到哪些因素影响?...
  2. KNN、MOG2和GMG
  3. python入门有基础-python入门基础
  4. Go加密解密之DES
  5. java高分面试指南:javamvc模式简单案例
  6. 解析底层原理!月薪20k+的Android面试都问些什么?深夜思考
  7. mysql 同一帐号多次登录_freeradius2.1.3 防止用户帐号重复登录
  8. Array with Odd Sum(CF-1296A)
  9. mysql 数据库表重建_mysql 数据库表重建
  10. 让VS2010支持Windows2000
  11. 软件one pin错误是啥意思_理想ONE,从交车就开始道歉,是有礼貌的新势力
  12. 39-如何共享数据?
  13. 表关联关系,表的复制
  14. spark filter过滤某个字段在几个值里面_Spark案例学习-PV的统计
  15. 二(高)阶多元微分方程数值解法(其一)
  16. 备考cfa一、二级的方法和资料
  17. 计算机在条形码的应用,条码应用
  18. 破解安装谷歌翻译软件 Translate Client ( 含文件下载链接 )
  19. 计算机一些常用快捷指令
  20. 绘制一幅蓝图_给未来画一幅蓝图

热门文章

  1. java 甘特图_Java报表软件--甘特图(Gantt chart)深度解析
  2. (绝对防御勒索病毒)装机员 ghost win7 Sp1 64位纯净6月版
  3. winrar解压缩中文文件夹乱码的最简单解决办法
  4. fmri学习笔记|SPM 代码 循环
  5. 我国电子商务五大发展阶段
  6. 【rustdesk】windows安装vcpkg及配置, rustdesk 客户端依赖库安装
  7. java程序员面试笔试宝典8.3排序
  8. 深入分析中小型千兆网吧解决方案(转)
  9. PAT乙级2021秋季复盘
  10. Java 拦截器重定向无限循环/重定向次数过多报错 的解决方案