当前位置:   article > 正文

Java 集合学习之Set(HashSet&LinkedHashSet&TreeSet)_linkedhashset set = new hashset<>();

linkedhashset set = new hashset<>();

一、Set集合类思维导图

 Set:不允许重复的集合。不会有多个元素引用相同的对象。

二、HashSet、LinkedHashSet及TreeSet区别简述

  • HashSet:继承AbstractSet类,实现Set、Cloneable、Serializable接口,非线程安全,无序
  1. public class HashSet<E> extends AbstractSet<E>
  2. implements Set<E>, Cloneable, java.io.Serializable
  • LinkedHashSet:继承HashSet,实现Set、Cloneable、java.io.Serializable接口,非线程安全,有序(顺序)
  1. public class LinkedHashSet<E> extends HashSet<E>
  2. implements Set<E>, Cloneable, java.io.Serializable {
  • TreeSet:继承AbstractSet,实现NavigableSet、Cloneable、java.io.Serializable接口,非线程安全,有序(排序)
  1. public class TreeSet<E> extends AbstractSet<E>
  2. implements NavigableSet<E>, Cloneable, java.io.Serializable

三、详解及示例说明

  • HashSet

HashSet底层是基于HashMap实现的,元素是无序的,添加、删除操作复杂度跟HashMap都是o(1)。Map——传送门
部分源码如图。

  1. // Dummy value to associate with an Object in the backing Map
  2. private static final Object PRESENT = new Object();
  3. //默认构造函数,实例化一个空的HashMap对象,初始容量16,负载因子0.75
  4. public HashSet() {
  5. map = new HashMap<>();
  6. }
  7. //带参数构造函数
  8. public HashSet(Collection<? extends E> c) {
  9. map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
  10. addAll(c);
  11. }
  12. public HashSet(int initialCapacity, float loadFactor) {
  13. map = new HashMap<>(initialCapacity, loadFactor);
  14. }
  15. public HashSet(int initialCapacity) {
  16. map = new HashMap<>(initialCapacity);
  17. }
  18. //采用map.keySet()的迭代器
  19. public Iterator<E> iterator() {
  20. return map.keySet().iterator();
  21. }
  22. public int size() {
  23. return map.size();
  24. }
  25. public boolean isEmpty() {
  26. return map.isEmpty();
  27. }
  28. //set里面放的key-value的key,这里是containsKey
  29. public boolean contains(Object o) {
  30. return map.containsKey(o);
  31. }
  32. //add方法,是把Map键值对的形式,构造PRESENT为null添加进去
  33. public boolean add(E e) {
  34. return map.put(e, PRESENT)==null;
  35. }
  36. public void clear() {
  37. map.clear();
  38. }

从上面源码中可以看出,当HashSet集合中添加元素(add方法)时,相当于Map调用put方法,而Map的put方法要求key不能重复。其实本质上是调用对象的HashCode()方法,来获得对象的HashCode值,根据HashCode值决定存放位置。在Java中,每个对象都有HashCode()方法,存储的是对象在JVM堆上的内存地址,并且都不相同,HashMap中重写了hashCode()和equals() 方法,感兴趣可以去研究下。

public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}

Java规范中

重写equals方法,必须重写hashCode方法,对象相同即equals返回true,hashCode必须相同,如果对象不相同即equals返回false,hashCode可能相同。

  1. 对象相等,hashcode一定相等,equals返回true
  2. hashCode值相等,对象不一定相等,因为equals可能返回false
  3. hashCode值不等,对象一定不等
  4. 所以equals被覆盖,则hashCode方法也必须被覆盖
  5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。

HashSet常用方法代码示例

  1. package Set;
  2. import java.util.HashSet;
  3. import java.util.Iterator;
  4. public class HashSetDemo {
  5. public static void main(String[] args) {
  6. HashSet<Integer> set = new HashSet<>();
  7. set.add(1);
  8. set.add(3);
  9. set.add(3);
  10. //添加重复元素
  11. System.out.println("添加重复元素:" + set.add(1));
  12. //输出set大小
  13. System.out.println("set大小:" + set.size());
  14. //判断是否包含值
  15. System.out.println("是否包含1:" + set.contains(1));
  16. //删除值3
  17. set.remove(3);
  18. System.out.println("删除3之后的set:" + set);
  19. set.add(88);
  20. set.add(666);
  21. //遍历set
  22. //1、foreach遍历
  23. for (Integer num : set) {
  24. System.out.print(num + " ");
  25. }
  26. System.out.println();
  27. //2、迭代器遍历
  28. Iterator<Integer> it = set.iterator();
  29. while (it.hasNext()) {
  30. System.out.print(it.next() + " ");
  31. }
  32. }
  33. }
  • LinkedHashSet

继承HashSet,底层由哈希表+双向链表组成,保证了迭代顺序,元素有序且唯一,存取顺序一致,类似于LinkedHashMap,只不过保证元素不重复。

  1. //四个构造函数
  2. public LinkedHashSet(int initialCapacity, float loadFactor) {
  3. super(initialCapacity, loadFactor, true);
  4. }
  5. public LinkedHashSet(int initialCapacity) {
  6. super(initialCapacity, .75f, true);
  7. }
  8. public LinkedHashSet() {
  9. super(16, .75f, true);
  10. }
  11. public LinkedHashSet(Collection<? extends E> c) {
  12. super(Math.max(2*c.size(), 11), .75f, true);
  13. addAll(c);
  14. }
  15. //上面构造函数中的super函构造函数
  16. HashSet(int initialCapacity, float loadFactor, boolean dummy) {
  17. //实例化一个LinkedHashMap
  18. map = new LinkedHashMap<>(initialCapacity, loadFactor);
  19. }

LinkedHashSet没有自己方法,所有方法继承自HashSet,唯一不同的是采用不同对象存储元素,LinkedHashSet存储的是LinkedHashMap的键。

LinkedHashSet常用方法代码示例

  1. package Set;
  2. import java.util.LinkedHashSet;
  3. public class LinkedHashSetDemo {
  4. public static void main(String[] args) {
  5. LinkedHashSet<Integer> lset = new LinkedHashSet<>();
  6. lset.add(1);
  7. lset.add(88);
  8. lset.add(66);
  9. //重复元素
  10. System.out.println("重复元素添加:" + lset.add(88));
  11. ;
  12. lset.add(9999);
  13. //foreach遍历,可以看到结果按插入顺序输出
  14. for (Integer num : lset) {
  15. System.out.print(num + " ");
  16. }
  17. }
  18. }
  • TreeSet

底层是红黑树,非线程安全,对元素排序时,如果采用空参构造函数则自然排序,否则采用比较器排序。 类似TreeMap

默认自然排序示例代码如下

  1. package Set;
  2. import java.util.Iterator;
  3. import java.util.TreeSet;
  4. public class TreeSetDemo {
  5. public static void main(String[] args) {
  6. TreeSet<Integer> set = new TreeSet<>();
  7. set.add(8);
  8. set.add(1);
  9. set.add(6);
  10. set.add(2);
  11. Iterator<Integer> it = set.iterator();
  12. //按照key值排序过的
  13. while (it.hasNext()) {
  14. System.out.print(it.next() + " ");
  15. }
  16. }
  17. }

这里key为什么能够自然排序,其实是Integer类继承了Comparable接口 

public final class Integer extends Number implements Comparable<Integer> 

 如果要排序的key值类型没有实现排序,那么程序就会运行报错,如下图会报错

  1. package Set;
  2. import java.util.Iterator;
  3. import java.util.TreeSet;
  4. public class TreeSetDemo {
  5. static class bird {
  6. int age;
  7. public bird(int age) {
  8. this.age = age;
  9. }
  10. }
  11. public static void main(String[] args) {
  12. TreeSet<bird> set = new TreeSet<>();
  13. set.add(new bird(8));
  14. set.add(new bird(1));
  15. set.add(new bird(2));
  16. set.add(new bird(6));
  17. Iterator<bird> it = set.iterator();
  18. ///这里key值是bird类,本身是无序的,虽然编译没错,但是TreeSet是有序的
  19. //所以这样运行时会报错的bird cannot be cast to java.lang.Comparable
  20. while (it.hasNext()) {
  21. System.out.print(it.next() + " ");
  22. }
  23. }
  24. }

 Comparable接口排序示例代码如下

  1. package Set;
  2. import java.util.Iterator;
  3. import java.util.TreeSet;
  4. public class TreeSetDemo {
  5. static class bird implements Comparable<bird> {
  6. int age;
  7. public bird(int age) {
  8. this.age = age;
  9. }
  10. public int getAge() {
  11. return this.age;
  12. }
  13. @Override
  14. public int compareTo(bird b) {
  15. return this.age - b.age;
  16. }
  17. }
  18. public static void main(String[] args) {
  19. TreeSet<bird> set = new TreeSet<>();
  20. set.add(new bird(8));
  21. set.add(new bird(1));
  22. set.add(new bird(2));
  23. set.add(new bird(6));
  24. Iterator<bird> it = set.iterator();
  25. ///这里key值是bird类,本身是无序的,虽然编译没错,但是TreeSet是有序的
  26. //所以这样运行时会报错的bird cannot be cast to java.lang.Comparable
  27. while (it.hasNext()) {
  28. System.out.print(it.next().getAge() + " ");
  29. }
  30. }
  31. }

未完待续......

本博客是个人学习总结,参考学习了很多知识(网络&源码&书),如果内容有误或者有资料参考未列举出来,欢迎交流~

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

2019.12.14 更新

set,map,list相互转换

  1. Map<String, String> map = new HashMap<String, String>();
  2. map.put("sss", "sssss");
  3. List<String> list = new ArrayList<String>(map.keySet());
  4. System.out.println(list);
  5. list.add("sss");
  6. System.out.println(list);
  7. Set<String>set=new HashSet<String>(list);
  8. System.out.println(set);

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/866503
推荐阅读
相关标签
  

闽ICP备14008679号