持续学习&持续更新中…
【恋上数据结构与算法 第一季】集合 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);
}
});
}
}
参考
小码哥李明杰老师课程: 恋上数据结构与算法 第一季.
本文完,感谢您的关注支持!