HashSet

首先来看下HashSet的add()这个方法的源代码:

public boolean add(E e) {
return map.put(e, PRESENT)==null;
}

由此可知HashSet的值是存储在一个Map的key里面的,而正好Map的key是不能重复的,以下是HashSet的部分源码:

 private transient HashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object(); /**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<E,Object>();
} public Iterator<E> iterator() {
return map.keySet().iterator();
} public int size() {
return map.size();
}

接着再看看HashMap里的put()方法是如何判断值是否重复的,以下是HashMap的put()源码:

public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
} modCount++;
addEntry(hash, key, value, i);
return null;
}

关键这句:if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

它先进行了hashCode的判断,如果hashCode相等再接着使用==来判断传入的对象的引用地址是否相等及使用传入对象的equals()方法来进行判断。

接着再看看HashSet的contains()方法的源码:

public boolean contains(Object o) {
return map.containsKey(o);
}

调用的是Map的containsKey()方法,以下是HashMap的containsKey()源码:

public boolean containsKey(Object key) {
return getEntry(key) != null;
} final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k)))) 和put()方法的判断是一样的。

TreeSet
看看TreeSet的add()方法的源码:
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}

它也同样是由一个Map的key来进行存储,看看这个Map:

/**
* The backing map.
*/
private transient NavigableMap<E,Object> m;

接着看看TreeSet的构造函数:

TreeSet(NavigableMap<E,Object> m) {
this.m = m;
} public TreeSet() {
this(new TreeMap<E,Object>());
}

TreeSet的无參构造函数里实例化了一个TreeMap对象,下面看看这个TreeMap对象:

public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{ private final Comparator<? super K> comparator; public TreeMap() {
comparator = null;
} public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
//其他略...
}
public interface NavigableMap<K,V> extends SortedMap<K,V>

接着看这个TreeMap的put()方法:

public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
// TBD:
// 5045147: (coll) Adding null to an empty TreeSet should
// throw NullPointerException
//
// compare(key, key); // type check
root = new Entry<K,V>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<K,V>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}

TreeMap存储的对象是会进行排序的,所以存储的对象都必须实现Comparable接口。

TreeMap先判断Comparator对象是否为空,不为空则使用当前的Comparator对象的compare方法来进行比较排序,若发现compareTo返回的值为0,则认为这两个对象是重复的了,然后覆盖原来的value;如果Comparator对象为空,则使用集合对象实现的Comparable接口的compareTo方法来进行比较排序,同样地当返回值是0时就认为对象重复,然后覆盖原来的value。

 

最新文章

  1. for循环每次取出一个字符(不是字节)
  2. SVN+码云 简单使用流程
  3. iOS开发-应用崩溃日志揭秘(一)
  4. 【转】Java出现No enclosing instance of type E is accessible. Must qualify the allocation with an enclosing
  5. iOS 中 为UIView添加背景图片
  6. MySQL日期数据类型、时间类型使用总结
  7. Word 2013双引号的BUG
  8. 被druid折磨的够呛
  9. scheme 模拟queue
  10. swift 笔记 (十九) —— 协议
  11. vuex store刷新存储状态
  12. Android 提高 gradle 的编译速度
  13. 自学huawei之路-AC6005版本升级步骤
  14. Github安全整理(转载)
  15. linux 定时任务 日志记录
  16. 不用MathType, 如何在Mac Word中插入公式
  17. 53.纯 CSS 创作一个文本淡入淡出的 loader 动画
  18. 使用iTEXT库生成pdf
  19. ThreadLocal 详解
  20. Vue Ssr之旅 —— Nuxt

热门文章

  1. Docker私有仓库的构建
  2. Manjaro安装配置美化字体模糊发虚解决记录
  3. css--小白入门篇1
  4. 阿里云ECS屏蔽25端口,官方建议使用465 SSL端口发送邮件
  5. python_ 学习笔记(hello world)
  6. vue-cli项目结构分析
  7. C语言结构体用法
  8. Django DTL模板语法中的过滤器
  9. gnuplot examples
  10. extract a page from a multiple pages pdf on Ubuntu OS