【恋上数据结构与算法 第一季】集合 Set


持续学习&持续更新中…


集合

在这里插入图片描述

集合没有索引的概念(没有set.get(index))

接口设计

public interface Set<E> {
    
    
    int size();
    boolean isEmpty();
    void clear();
    boolean contains(E element);
    void add(E element);
    void remove(E element);
    void traversal(Visitor<E> visitor);

    // Set没有索引的概念,因此需要提供遍历接口
    public static abstract class Visitor<E> {
    
    
        boolean stop;
        public abstract boolean visit(E element);
    }
}

使用List实现Set—ListSet

public class ListSet<E> implements Set<E> {
    
    

    private final List<E> list = new LinkedList<>();

    @Override
    public int size() {
    
    
        return list.size();
    }

    @Override
    public boolean isEmpty() {
    
    
        return list.isEmpty();
    }

    @Override
    public void clear() {
    
    
        list.clear();
    }

    @Override
    public boolean contains(E element) {
    
    
        return list.contains(element);
    }

    @Override
    public void add(E element) {
    
    
        int index = list.indexOf(element);
        if (index == List.ELEMENT_NOT_FOUND) {
    
     // 找不到该元素,添加
            list.add(element);
        } else {
    
     // 可以找到说明该元素存在,可以替换该元素或者什么都不管
            list.set(index, element);
        }
    }

    @Override
    public void remove(E element) {
    
    
        if (list.indexOf(element) != List.ELEMENT_NOT_FOUND) {
    
    
            list.remove(element);
        }
    }

    @Override
    public void traversal(Visitor<E> visitor) {
    
    
        for (int i = 0, size = list.size(); i < size; i++) {
    
    
            if (visitor.visit(list.get(i))) return;
        }
    }

}

使用RedBlackTree实现Set—TreeSet

public class TreeSet<E> implements Set<E> {
    
    

    private final RedBlackTree<E> tree;

    public TreeSet() {
    
    
        tree = new RedBlackTree<>();
    }

    public TreeSet(Comparator<E> comparator) {
    
    
        tree = new RedBlackTree<>(comparator);
    }

    @Override
    public int size() {
    
    
        return tree.size();
    }

    @Override
    public boolean isEmpty() {
    
    
        return tree.isEmpty();
    }

    @Override
    public void clear() {
    
    
        tree.clear();
    }

    @Override
    public boolean contains(E element) {
    
    
        return tree.contains(element);
    }

    @Override
    public void add(E element) {
    
    
    // 二叉搜索树有天然的去重功能
    // 红黑树内自动会覆盖相等的元素
        tree.add(element); 
    }

    @Override
    public void remove(E element) {
    
    
        tree.remove(element);
    }

    @Override
    public void traversal(Visitor<E> visitor) {
    
    
        tree.inorderTraversal(new Tree.Visitor<E>() {
    
     // 中序遍历:从小到大
            @Override
            public void visit(E element) {
    
    
                visitor.visit(element);
            }
        });
    }

}

TreeSet与ListSet的性能测试

test:

    static void testSet(Set<String> set, String[] words) {
    
    
        for (int i = 0; i < words.length; i++) {
    
    
            set.add(words[i]);
        }
        for (int i = 0; i < words.length; i++) {
    
    
            set.contains(words[i]);
        }
        for (int i = 0; i < words.length; i++) {
    
    
            set.remove(words[i]);
        }
    }

    static void test() {
    
    
        FileInfo fileInfo = Files.read("C:\\Users\\lpruoyu\\Desktop\\src\\java\\util\\concurrent",
                new String[]{
    
    "java"});
        System.out.println("文件数量:" + fileInfo.getFiles());
        System.out.println("代码行数:" + fileInfo.getLines());
        String[] words = fileInfo.words();
        System.out.println("未去重单词数量:" + words.length);

        Times.test("ListSet", new Times.Task() {
    
    
            public void execute() {
    
    
                testSet(new ListSet<>(), words);
            }
        });

        Times.test("TreeSet", new Times.Task() {
    
    
            public void execute() {
    
    
                testSet(new TreeSet<>(), words);
            }
        });
    }

输出:

文件数量:364
代码行数:212251
未去重单词数量:878279
【ListSet】
开始:00:34:23.611
结束:00:35:19.719
耗时:56.108秒
-------------------------------------
【TreeSet】
开始:00:35:19.720
结束:00:35:20.288
耗时:0.568秒
-------------------------------------

由此可见,TreeSet的性能比ListSet好很多。

复杂度分析

  • 由于链表本身的add、remove、contains都是O(n),因此ListSet的那几个方法也都是O(n)级别的。

  • 由于红黑树本身的add、remove、contains都是O(logn),因此TreeSet的那几个方法也都是O(logn)级别的。

TreeSet的不足(限制)

RedBlackTree作为二叉搜索树的一种,使用RedBlackTree实现的TreeSet有一个限制(被约束):添加的元素必须具备可比较性

如何解决?

使用之后学习的哈希表

Map与Set

在这里插入图片描述

在这里插入图片描述

TreeSet

public class TreeSet<E> implements Set<E> {
    
    

    private final Map<E, Object> map;
    private Comparator<E> comparator;

    public TreeSet() {
    
    
        this(null);
    }

    public TreeSet(Comparator<E> comparator) {
    
    
        this.comparator = comparator;
        map = new TreeMap<>(comparator);
    }

    @Override
    public int size() {
    
    
        return map.size();
    }

    @Override
    public boolean isEmpty() {
    
    
        return map.isEmpty();
    }

    @Override
    public void clear() {
    
    
        map.clear();
    }

    @Override
    public boolean contains(E element) {
    
    
        return map.containsKey(element);
    }

    @Override
    public void add(E element) {
    
    
        map.put(element, null);
    }

    @Override
    public void remove(E element) {
    
    
        map.remove(element);
    }

    @Override
    public void traversal(Visitor<E> visitor) {
    
    
        map.traversal(new Map.Visitor<E, Object>() {
    
    
            @Override
            public boolean visit(E key, Object value) {
    
    
                return visitor.visit(key);
            }
        });
    }
}

HashSet

public class HashSet<E> implements Set<E> {
    
    

    private final Map<E, Object> map = new HashMap<>();

    @Override
    public int size() {
    
    
        return map.size();
    }

    @Override
    public boolean isEmpty() {
    
    
        return map.isEmpty();
    }

    @Override
    public void clear() {
    
    
        map.clear();
    }

    @Override
    public boolean contains(E element) {
    
    
        return map.containsKey(element);
    }

    @Override
    public void add(E element) {
    
    
        map.put(element, null);
    }

    @Override
    public void remove(E element) {
    
    
        map.remove(element);
    }

    @Override
    public void traversal(Visitor<E> visitor) {
    
    
        map.traversal(new Map.Visitor<E, Object>() {
    
    
            @Override
            public boolean visit(E key, Object value) {
    
    
                return visitor.visit(key);
            }
        });
    }

}

LinkedHashSet

public class LinkedHashSet<E> implements Set<E> {
    
    

    private final Map<E, Object> map = new LinkedHashMap<>();

    @Override
    public int size() {
    
    
        return map.size();
    }

    @Override
    public boolean isEmpty() {
    
    
        return map.isEmpty();
    }

    @Override
    public void clear() {
    
    
        map.clear();
    }

    @Override
    public boolean contains(E element) {
    
    
        return map.containsKey(element);
    }

    @Override
    public void add(E element) {
    
    
        map.put(element, null);
    }

    @Override
    public void remove(E element) {
    
    
        map.remove(element);
    }

    @Override
    public void traversal(Visitor<E> visitor) {
    
    
        map.traversal(new Map.Visitor<E, Object>() {
    
    
            @Override
            public boolean visit(E key, Object value) {
    
    
                return visitor.visit(key);
            }
        });
    }

}

参考

小码哥李明杰老师课程: 恋上数据结构与算法 第一季.


本文完,感谢您的关注支持!


おすすめ

転載: blog.csdn.net/weixin_44018671/article/details/121462561