HashSet源码:
HashSet是无须且不重复的、非线程安全的、底层用HashMap(哈希表)实现的。接下来先看下HashSet的使用:
//HashSet的存储过程: //1.根据hashCode计算保存的位置,如果位置为空,则直接保存,否则执行第二步。 //2.执行equals方法,如果方法返回true,则认为是重复,拒绝存储,否则形成链表。 public static void main(String[] args) { HashSet<Person> hashSet=new HashSet<>(); Person p1=new Person("tang",21); Person p2=new Person("he", 22); Person p3=new Person("yu", 21); //1.添加元素 hashSet.add(p1); hashSet.add(p2); hashSet.add(p3); //重复,添加失败 hashSet.add(p3); //直接new一个相同属性的对象,依然会被添加,。 //假如相同属性便认为是同一个对象,怎么修改?应该去重写equal方法 hashSet.add(new Person("yu", 21)); System.out.println(hashSet.toString()); //2.删除元素 hashSet.remove(p2); //3.遍历 //3.1 增强for for (Person person : hashSet) { System.out.println(person); } //3.2 迭代器 Iterator<Person> iterator=hashSet.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } //4.判断 System.out.println(hashSet.isEmpty()); //直接new一个相同属性的对象结果输出是false,不难理解。 //注:假如相同属性便认为是同一个对象,该怎么做? System.out.println(hashSet.contains(new Person("tang", 21))); }
构造函数:
//无参构造:默认初始容量是16,加载因子是0.75 public HashSet() { map = new HashMap<>(); } //将collection转换为一个HashSet集合 public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } //指定初始化容量和加载因子的大小 public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } //指定初始化容量 加载因子用默认的 public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); } //default权限的构造方法,只能在包内可见,这个作用是给HashSet的子类 LinkedHashSet用的,LinkedHashSet的构造方法也是调用HashSet中的构造方法,就是调用这个构造方法,
//来使其底层的实现原理为LinkedHashMap,而不是HashMap HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }
新增:
//1.将新增的e放在map的key上,再次新增e会覆盖掉这就是set为啥不重复! 2.PRESENT是为了满足map的key-value格式而创建的假值,map在添加一个key-value时若原来有值则返回旧值,无返回null public boolean add(E e) { return map.put(e, PRESENT)==null; } private static final Object PRESENT = new Object();
删除:
public boolean remove(Object o) { return map.remove(o)==PRESENT; }
其他:
//contains(Object) public boolean contains(Object o) { return map.containsKey(o); } //isEmpty public boolean isEmpty() { return map.isEmpty(); } //clone 复制原HashSet的浅表副本,只复制其中元素的值,不复制值所对应的引用 //就是两者毫无关系,仅仅是他们元素的值相同,这里要明白的是 值传递和引用传递。 public Object clone() { try { HashSet<E> newSet = (HashSet<E>) super.clone(); newSet.map = (HashMap<E, Object>) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(e); } } //迭代器:在HashMap中专门给Hashset实现了迭代器 public Iterator<E> iterator() { return map.keySet().iterator(); }
LinkedHashSet源码:
- 上面说到它是HashSet的子类拥有HashSet的所有方法,底层是LinkedHashMap,它源码里面只有四个个构造函数最终都是通过构造函数HashSet中的new LinkedHashMap<>(initialCapacity, loadFactor)创建。
- LinkedHashSet能按元素插入顺序进行迭代,也就是说,跟HashSet唯一的区别就是能够保证集合中元素的顺序,但是其保存的位置是变化的,比如:往LinkedHashSet中插入A,B,C,D, 这四个元素其保存的位置可能是变化的,但是在输出的时候能够按照插入的顺序输出。 HashSet就不能保证其集合中元素的顺序,应为HashMap每次将元素插入的位置度不确定,并且元素度是放在桶中的,每次遍历度是从第一个桶开始遍历,但是第一个桶中的元素也是不固定的,所以说HashSet不能保证其集合中元素的顺序。
TreeSet源码:
- 类似于HashSet源码分析,treeSet底层维护了TreeMap(红黑树结构)。
- treeSet中的元素必须实现Comparable接口并重写compare To方法来实现有顺序和不重复。
- TreeSet由于实现了顺序导致其读取速度没有HashSet/LinkedHashSet快。
public static void main(String[] args) { TreeSet<Person> persons=new TreeSet<Person>(); Person p1=new Person("tang",21); Person p2=new Person("he", 22); Person p3=new Person("yu", 21); //1.添加元素 persons.add(p1); persons.add(p2); persons.add(p3); //注:直接添加会报类型转换错误,需要实现Comparable接口 System.out.println(persons.toString()); //2.删除元素 persons.remove(p1); persons.remove(new Person("he", 22)); System.out.println(persons.toString()); //对于TreSet中元素必须实现Comparable接口有两种方式: 1.在元素类实现接口重写方法 public class Person implements Comparable<Person>{ @Override //1.先按姓名比 //2.再按年龄比 public int compareTo(Person o) { int n1=this.getName().compareTo(o.getName()); int n2=this.age-o.getAge(); return n1==0?n2:n1; } } //2.匿名内部类: TreeSet<Person> persons=new TreeSet<Person>(new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { // 先按年龄比较 // 再按姓名比较 int n1=o1.getAge()-o2.getAge(); int n2=o1.getName().compareTo(o2.getName()); return n1==0?n2:n1; } }); Person p1=new Person("tang",21); Person p2=new Person("he", 22); Person p3=new Person("yu", 21); persons.add(p1); persons.add(p2); persons.add(p3); System.out.println(persons.toString()); }