美文网首页android成神之路Android知识Android开发
Android中的数据结构解析(二)HashSet、Linked

Android中的数据结构解析(二)HashSet、Linked

作者: 愚蠢的高小星 | 来源:发表于2016-10-20 23:16 被阅读1089次

    1.Set接口
    Set接口和上节讲到的List接口都继承了Collection接口,而Set接口和List接口最大的不同就是List中的元素是可以重复的,而Set中的元素不能重复,所以Set中的元素必须定义equals方法来保证元素的唯一性。除此之外,List接口放入元素是有序的,在使用时可以通过下标来找到对应位置的元素,而Set不保证放入元素的顺序,当然,仅仅是不保证而已,Set也是有维护了元素顺序的实现类的。接下来看一下Set的常见实现类:HashSet、TreeSet、LinkedHashSet。

    2.HashSet
    HashSet实现Set接口,基于HashMap实现,底层使用HashMap保存数据。HashSet的实现其实很简单,基本就是封装了一下HashMap,看一下几个主要方法的源码:
    构造方法:

    public HashSet() {
            this(new HashMap<E, HashSet<E>>());
        }
    
    public HashSet(int capacity) {
            this(new HashMap<E, HashSet<E>>(capacity));
        }
    

    可以看到使用了HashMap作为容器。

    常用方法:

    @Override
        public boolean add(E object) {
            return backingMap.put(object, this) == null;
        }
    
    @Override
        public boolean isEmpty() {
            return backingMap.isEmpty();
        }
    
    @Override
        public boolean remove(Object object) {
            return backingMap.remove(object) != null;
        }
    
    @Override
        public int size() {
            return backingMap.size();
        }
    
    //HashSet中的元素作为key保存在HashMap中,所以HashSet的迭代器使用了HashMap的keySet返回所有的key
    @Override
        public Iterator<E> iterator() {
            return backingMap.keySet().iterator();
        }
    

    很简单,都是HashMap的基本方法,在理解了HashMap后,就能很容易明白HashSet方法的实现。

    @Override
        public boolean contains(Object object) {
            return backingMap.containsKey(object);
        }
    

    这里又提到了contains方法。上一节在讲到ArrayList的时候提过,如果要正确比较ArrayList中的两个元素是否相同,需要重写equals方法。而对于HashSet,除了要重写存入对象的equals方法外,还需要重写hashCode方法,保证HashSet中没有相同的两个对象,这点是和HashMap一样的。

    总结一下HashSet的特点:HashSet中不能有相同的元素,可以有一个null元素,存入的元素是无序的。简单的说,其实HashSet就是操作了HashMap的key,并且同时具有Set接口的性质。

    3.LinkedHashSet
    LinkedHashSet是HashSet的子类,它和HashSet的区别就在于LinkedHashSet的元素严格按照放入顺序排列。LinkedHashSet内部使用LinkedHashMap实现,所以它和HashSet的关系就相当于HashMap和LinkedHashMap的关系。如果你想让取出元素的顺序和插入元素的顺序完全相同,那么就使用LinkedHashSet代替HashSet。

    4.TreeSet
    上面提到HashSet是用HashMap实现的,其实这里的TreeSet也是用Map接口的另一个实现类TreeMap实现的。TreeMap是一个有序的二叉树。TreeSet实现了SortedSet接口,其特点是会对放入其中的元素进行排序,和LinkedHashSet不同的是,LinkedHashSet是根据元素的放入顺序进行排序,而TreeSet是根据元素的自然顺序进行排序。
    例如:

    TreeSet<Integer> set = new TreeSet<Integer>();
    set.add(3);
    set.add(2);
    set.add(5);
    set.add(3);
    set.add(1);
    set.add(5);
    System.out.println(Arrays.toString(set.toArray()));
    //打印出来的结果[1, 2, 3, 5]
    

    不要问我为什么只有4个数,往上翻翻看看Set的性质(敲黑板!!)
    同样的,如果往TreeSet中放入String类型的数据,输出时也会按照英文字母的顺序输出,这就是前面说的,TreeSet会将放入的元素按照自然顺序排列。

    那么,如果放入的元素是自定义的对象呢?
    很简单,之所以TreeSet能对Integer和String类型的数据进行排序,是因为Integer和String都实现Comparable接口并实现了compareTo方法。那么如果我们自定义的对象要放入TreeSet实现排序,也只需要实现Comparable接口并自定义compareTo方法,来实现让TreeSet中的对象按照我们希望的规则排列。

    另一种方法,在TreeSet的构造方法中,可以传入一个Comparator

    public TreeSet(Comparator<? super E> comparator) {
            backingMap = new TreeMap<E, Object>(comparator);
        }
    

    可以在这个Comparator中定义比较的规则,来对传入的对象进行排序。

    从上面对TreeSet性质的叙述中,我们不难得出另一个结论:TreeSet中不允许放入null元素。

    实际开发中,TreeSet的使用频率较低,是因为TreeSet每插入一个数据,就会排一次序,导致性能降低,而一般我们都是放入一堆数据后再一起排序,所以用不到TreeSet。但如果刚好碰到需要进行插入后即时排序的需求,那这时候就可以用上TreeSet了~

    5.总结
    在Set接口的实现类中,HashSet是一种元素不重复且无序的存储容器,可以存储一个null元素,放入HashSet的对象需要重写equals和hashCode方法以保证存入对象唯一;LinkedHashSet是HashSet的子类,具有HashSet的性质,且它保存了元素放入的顺序;TreeSet可以将存入的元素按照一定的规则排序,对象放入TreeSet前需要实现compareTo方法来定义这个规则,TreeSet中不能存在null元素。

    相关文章

      网友评论

      本文标题:Android中的数据结构解析(二)HashSet、Linked

      本文链接:https://www.haomeiwen.com/subject/lxbtuttx.html