集合源码-Set

 集合源码-Set

 

 

 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());
    }

 

上一篇:SQL WHERE 子句


下一篇:7. 列表渲染