码迷,mamicode.com
首页 > 其他好文 > 详细

集合 源码分析判断是否是同一元素的策略

时间:2018-07-13 20:24:18      阅读:169      评论:0      收藏:0      [点我收藏+]

标签:swa   als   1.2   private   --   内容   ceo   not   lin   

List:equals

对于List集合(ArrayList、LinkedList等):仅仅是通过判断两个对象的【equals】方法是否为true。
以下为 ArrayList 的部分源码:
public boolean contains(Object o) {
   return indexOf(o) >= 0;
}

public int indexOf(Object o) {
   if (o == null) {
      for (int i = 0; i < size; i++)
         if (elementData[i] == null)
            return i;
   } else {
      for (int i = 0; i < size; i++)
         if (o.equals(elementData[i])) //核心代码:contains 时完全是根据 equals 方法来判断是否是同一元素
            return i;
   }
   return -1;
}
 
1
public boolean contains(Object o) {
2
   return indexOf(o) >= 0;
3
}
4
5
public int indexOf(Object o) {
6
   if (o == null) {
7
      for (int i = 0; i < size; i++)
8
         if (elementData[i] == null)
9
            return i;
10
   } else {
11
      for (int i = 0; i < size; i++)
12
         if (o.equals(elementData[i])) //核心代码:contains 时完全是根据 equals 方法来判断是否是同一元素
13
            return i;
14
   }
15
   return -1;
16
}

Tree:compare 或 compareTo

对于Tree集合(TreeSet、TreeMap):
  • 如果构造指定了 Comparator,则根据 Comparator 接口中的 compare 方法返回值是否为 0 来判断。
  • 如果构造时没有指定 Comparator,则其中的元素必须实现 Comparable 接口,此时根据 compareTo 方法返回值是否为 0 来判断。

TreeSet、TreeMap部分源码:
public TreeSet() {
	this(new TreeMap<E,Object>());
}

public TreeSet(Comparator<? super E> comparator) {
	this(new TreeMap<>(comparator));
}

public boolean contains(Object o) {
	return m.containsKey(o);
}
x
1
public TreeSet() {
2
    this(new TreeMap<E,Object>());
3
}
4
5
public TreeSet(Comparator<? super E> comparator) {
6
    this(new TreeMap<>(comparator));
7
}
8
9
public boolean contains(Object o) {
10
    return m.containsKey(o);
11
}
12
public boolean containsKey(Object key) {
	return getEntry(key) != null;
}

final Entry<K,V> getEntry(Object key) {
	if (comparator != null) return getEntryUsingComparator(key); //核心代码:如果指定了比较器......
	if (key == null) throw new NullPointerException();
	@SuppressWarnings("unchecked")  //核心代码:如果没有指定比较器,则将元素强转为 Comparable
	Comparable<? super K> k = (Comparable<? super K>) key;
	Entry<K,V> p = root;
	while (p != null) {
	    int cmp = k.compareTo(p.key); 
	    if (cmp < 0) p = p.left;
	    else if (cmp > 0) p = p.right;
	    else return p; //核心代码:如果 compareTo 的值为0 ,则返回此值,否则继续遍历
	}
	return null;
}

final Entry<K,V> getEntryUsingComparator(Object key) {
	@SuppressWarnings("unchecked")
	K k = (K) key;
	Comparator<? super K> cpr = comparator;
	if (cpr != null) {
	    Entry<K,V> p = root;
	    while (p != null) {
		int cmp = cpr.compare(k, p.key);
		if (cmp < 0)  p = p.left;
		else if (cmp > 0) p = p.right;
		else  return p; //核心代码:如果 compare 的值为0 ,则返回此值,否则继续遍历
	    }
	}
	return null;
}
34
1
public boolean containsKey(Object key) {
2
    return getEntry(key) != null;
3
}
4
5
final Entry<K,V> getEntry(Object key) {
6
    if (comparator != null) return getEntryUsingComparator(key); //核心代码:如果指定了比较器......
7
    if (key == null) throw new NullPointerException();
8
    @SuppressWarnings("unchecked")  //核心代码:如果没有指定比较器,则将元素强转为 Comparable
9
    Comparable<? super K> k = (Comparable<? super K>) key;
10
    Entry<K,V> p = root;
11
    while (p != null) {
12
        int cmp = k.compareTo(p.key); 
13
        if (cmp < 0) p = p.left;
14
        else if (cmp > 0) p = p.right;
15
        else return p; //核心代码:如果 compareTo 的值为0 ,则返回此值,否则继续遍历
16
    }
17
    return null;
18
}
19
20
final Entry<K,V> getEntryUsingComparator(Object key) {
21
    @SuppressWarnings("unchecked")
22
    K k = (K) key;
23
    Comparator<? super K> cpr = comparator;
24
    if (cpr != null) {
25
        Entry<K,V> p = root;
26
        while (p != null) {
27
        int cmp = cpr.compare(k, p.key);
28
        if (cmp < 0)  p = p.left;
29
        else if (cmp > 0) p = p.right;
30
        else  return p; //核心代码:如果 compare 的值为0 ,则返回此值,否则继续遍历
31
        }
32
    }
33
    return null;
34
}

Hash:hashCode + equals

对于Hash系列的集合(TreeSet、HashMap):
  • 先判断元素的 hashCode 的值是否相同,如果不同则认为是不同的元素。
  • 如果两个元素的 hashCode 的值相同,则再判断元素的 equals 返回值是否为 true,如果为 true 则是不同的元素,否则为相同的元素。

HashSet、HashMap部分源码:
public HashSet() {
	map = new HashMap<>();
}
public boolean add(E e) {
	return map.put(e, PRESENT)==null;
}
public boolean contains(Object o) {
	return map.containsKey(o);
}
1
public HashSet() {
2
    map = new HashMap<>();
3
}
4
public boolean add(E e) {
5
    return map.put(e, PRESENT)==null;
6
}
7
public boolean contains(Object o) {
8
    return map.containsKey(o);
9
}
public boolean containsKey(Object key) {
	return getNode(hash(key), key) != null;
}

static final int hash(Object key) {
	int h;
	return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

final Node<K,V> getNode(int hash, Object key) {
	Node<K,V>[] tab; 
	Node<K,V> first, e; 
	int n; K k;
	if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) {
	    if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))  // always check first node
		return first;
	    if ((e = first.next) != null) {
		if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key);
		do {
		    if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e;
		} while ((e = e.next) != null);
	    }
	}
	return null;
}
x
1
public boolean containsKey(Object key) {
2
    return getNode(hash(key), key) != null;
3
}
4
5
static final int hash(Object key) {
6
    int h;
7
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
8
}
9
10
final Node<K,V> getNode(int hash, Object key) {
11
    Node<K,V>[] tab; 
12
    Node<K,V> first, e; 
13
    int n; K k;
14
    if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) {
15
        if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))  // always check first node
16
        return first;
17
        if ((e = first.next) != null) {
18
        if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key);
19
        do {
20
            if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e;
21
        } while ((e = e.next) != null);
22
        }
23
    }
24
    return null;
25
}

特殊对象:String

String 类重写了 hashCode 和 equals 方法,如果两个 String 的内容相同则 hashCode 和 equals 方法也相同或为 true。
private final char value[]; // The value is used for character storage. 
private int hash; // Default to 0

public int hashCode() {
	int h = hash;
	if (h == 0 && value.length > 0) { //如果长度为 0 ,则其 hash 也为 0
	    char val[] = value;
	    for (int i = 0; i < value.length; i++) {
		    h = 31 * h + val[i]; //哈希值完全就是对字符串中所有字符通过某种算法计算后的结果,而且算法是稳定的
	    }
	    hash = h;
	}
	return h;
}
x
1
private final char value[]; // The value is used for character storage. 
2
private int hash; // Default to 0
3
4
public int hashCode() {
5
    int h = hash;
6
    if (h == 0 && value.length > 0) { //如果长度为 0 ,则其 hash 也为 0
7
        char val[] = value;
8
        for (int i = 0; i < value.length; i++) {
9
            h = 31 * h + val[i]; //哈希值完全就是对字符串中所有字符通过某种算法计算后的结果,而且算法是稳定的
10
        }
11
        hash = h;
12
    }
13
    return h;
14
}
public boolean equals(Object anObject) {
	if (this == anObject) {
		return true;
	}
	if (anObject instanceof String) {  //必须是 String
		String anotherString = (String) anObject;
		int n = value.length;
		if (n == anotherString.value.length) {
			char v1[] = value;
			char v2[] = anotherString.value;
			int i = 0;
			while (n-- != 0) {
				if (v1[i] != v2[i]) return false; //逐个比较字符,一旦发现对应位置有不相同的,则为不同的字符串
				i++;
			}
			return true;
		}
	}
	return false;
}
x
1
20
 
1
public boolean equals(Object anObject) {
2
    if (this == anObject) {
3
        return true;
4
    }
5
    if (anObject instanceof String) {  //必须是 String
6
        String anotherString = (String) anObject;
7
        int n = value.length;
8
        if (n == anotherString.value.length) {
9
            char v1[] = value;
10
            char v2[] = anotherString.value;
11
            int i = 0;
12
            while (n-- != 0) {
13
                if (v1[i] != v2[i]) return false; //逐个比较字符,一旦发现对应位置有不相同的,则为不同的字符串
14
                i++;
15
            }
16
            return true;
17
        }
18
    }
19
    return false;
20
}

测试代码

List 集合系列测试代码

class Person {
	public String name;
	public int age;

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
	public boolean equals(Object o) {
		Person p = (Person) o;
		return this.age == p.age || this.name.equals(p.name);
	}

	@Override
	public String toString() {
		return "{" + name + "," + age + "}";
	}
}
x
1
class Person {
2
    public String name;
3
    public int age;
4
5
    public Person(String name, int age) {
6
        this.name = name;
7
        this.age = age;
8
    }
9
10
    @Override
11
    public boolean equals(Object o) {
12
        Person p = (Person) o;
13
        return this.age == p.age || this.name.equals(p.name);
14
    }
15
16
    @Override
17
    public String toString() {
18
        return "{" + name + "," + age + "}";
19
    }
20
}
List<Person> list = Arrays.asList(new Person("a", 20), new Person("a", 21), new Person("b", 20), new Person("b", 21));
System.out.println(list); // [{a,20}, {a,21}, {b,20}, {b,21}]
System.out.println(list.contains(new Person("c", 20)) + "  " + list.contains(new Person("a", 10086))); // true  true
x
 
1
List<Person> list = Arrays.asList(new Person("a", 20), new Person("a", 21), new Person("b", 20), new Person("b", 21));
2
System.out.println(list); // [{a,20}, {a,21}, {b,20}, {b,21}]
3
System.out.println(list.contains(new Person("c", 20)) + "  " + list.contains(new Person("a", 10086))); // true  true

Tree 集合系列测试代码

class Person implements Comparable<Person> {
	public String name;
	public int age;

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
	public boolean equals(Object p) {
		System.out.println("调用了equals方法,当前对象【" + toString() + "】被比较对象【" + p.toString() + "】");
		return this.name.equals(((Person) p).name);
	}

	@Override
	public String toString() {
		return "{" + name + "," + age + "}";
	}

	@Override
	public int compareTo(Person o) {
		System.out.println("调用了compareTo方法,当前对象【" + toString() + "】被比较对象【" + o.toString() + "】");
		return this.age - o.age;
	}
}
x
 
1
class Person implements Comparable<Person> {
2
    public String name;
3
    public int age;
4
5
    public Person(String name, int age) {
6
        this.name = name;
7
        this.age = age;
8
    }
9
10
    @Override
11
    public boolean equals(Object p) {
12
        System.out.println("调用了equals方法,当前对象【" + toString() + "】被比较对象【" + p.toString() + "】");
13
        return this.name.equals(((Person) p).name);
14
    }
15
16
    @Override
17
    public String toString() {
18
        return "{" + name + "," + age + "}";
19
    }
20
21
    @Override
22
    public int compareTo(Person o) {
23
        System.out.println("调用了compareTo方法,当前对象【" + toString() + "】被比较对象【" + o.toString() + "】");
24
        return this.age - o.age;
25
    }
26
}
如果构造时不指定 Comparator:
TreeSet<Person> set = new TreeSet<>();
System.out.println("添加第1个元素");
set.add(new Person("a", 20));
System.out.println("添加第2个元素"); // compareTo 方法返回值不为 0,判断为不同元素,所以添加进去
set.add(new Person("a", 21));
System.out.println("添加第3个元素"); // compareTo 方法返回值为 0,判断为相同元素,所以不添加进去
set.add(new Person("b", 20));
System.out.println("添加第4个元素"); // 相同元素,所以不添加进去
set.add(new Person("b", 21));
System.out.println(set);
x
 
1
TreeSet<Person> set = new TreeSet<>();
2
System.out.println("添加第1个元素");
3
set.add(new Person("a", 20));
4
System.out.println("添加第2个元素"); // compareTo 方法返回值不为 0,判断为不同元素,所以添加进去
5
set.add(new Person("a", 21));
6
System.out.println("添加第3个元素"); // compareTo 方法返回值为 0,判断为相同元素,所以不添加进去
7
set.add(new Person("b", 20));
8
System.out.println("添加第4个元素"); // 相同元素,所以不添加进去
9
set.add(new Person("b", 21));
10
System.out.println(set);
日志:
添加第1个元素
调用了compareTo方法,当前对象【{a,20}】被比较对象【{a,20}】
添加第2个元素
调用了compareTo方法,当前对象【{a,21}】被比较对象【{a,20}】
添加第3个元素
调用了compareTo方法,当前对象【{b,20}】被比较对象【{a,20}】//先和第一个元素比较,因为相同,所以直接结束
添加第4个元素
调用了compareTo方法,当前对象【{b,21}】被比较对象【{a,20}】//先和第一个元素比较,不相同,继续和下一个元素比较
调用了compareTo方法,当前对象【{b,21}】被比较对象【{a,21}】//再和第二个元素比较,因为相同,所以结束
[{a,20}, {a,21}]
x
1
添加第1个元素
2
调用了compareTo方法,当前对象【{a,20}】被比较对象【{a,20}】
3
添加第2个元素
4
调用了compareTo方法,当前对象【{a,21}】被比较对象【{a,20}】
5
添加第3个元素
6
调用了compareTo方法,当前对象【{b,20}】被比较对象【{a,20}】//先和第一个元素比较,因为相同,所以直接结束
7
添加第4个元素
8
调用了compareTo方法,当前对象【{b,21}】被比较对象【{a,20}】//先和第一个元素比较,不相同,继续和下一个元素比较
9
调用了compareTo方法,当前对象【{b,21}】被比较对象【{a,21}】//再和第二个元素比较,因为相同,所以结束
10
[{a,20}, {a,21}]

如果构造时指定 Comparator:
TreeSet<Person> set = new TreeSet<>((p1, p2) -> {
	System.out.println("调用了compare方法,对象1【" + p1.toString() + "】对象2【" + p2.toString() + "】");
	return p1.name.compareTo(p2.name);
});
x
1
TreeSet<Person> set = new TreeSet<>((p1, p2) -> {
2
    System.out.println("调用了compare方法,对象1【" + p1.toString() + "】对象2【" + p2.toString() + "】");
3
    return p1.name.compareTo(p2.name);
4
});
日志:
添加第1个元素
调用了compare方法,对象1【{a,20}】对象2【{a,20}】
添加第2个元素
调用了compare方法,对象1【{a,21}】对象2【{a,20}】
添加第3个元素
调用了compare方法,对象1【{b,20}】对象2【{a,20}】
添加第4个元素
调用了compare方法,对象1【{b,21}】对象2【{a,20}】
调用了compare方法,对象1【{b,21}】对象2【{b,20}】
[{a,20}, {b,20}]
11
 
1
添加第1个元素
2
调用了compare方法,对象1【{a,20}】对象2【{a,20}】
3
添加第2个元素
4
调用了compare方法,对象1【{a,21}】对象2【{a,20}】
5
添加第3个元素
6
调用了compare方法,对象1【{b,20}】对象2【{a,20}】
7
添加第4个元素
8
调用了compare方法,对象1【{b,21}】对象2【{a,20}】
9
调用了compare方法,对象1【{b,21}】对象2【{b,20}】
10
[{a,20}, {b,20}]

Hash 集合系列测试代码

class Person {
	public String name;
	public int age;

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
	public int hashCode() {
		System.out.println("调用了hashCode方法,当前对象【" + toString() + "】");
		return age;
	}

	@Override
	public boolean equals(Object p) {
		System.out.println("调用了equals方法,当前对象【" + toString() + "】被比较对象【" + p.toString() + "】");
		return this.name.equals(((Person) p).name);
	}

	@Override
	public String toString() {
		return "{" + name + "," + age + "}";
	}
}
26
1
class Person {
2
    public String name;
3
    public int age;
4
5
    public Person(String name, int age) {
6
        this.name = name;
7
        this.age = age;
8
    }
9
10
    @Override
11
    public int hashCode() {
12
        System.out.println("调用了hashCode方法,当前对象【" + toString() + "】");
13
        return age;
14
    }
15
16
    @Override
17
    public boolean equals(Object p) {
18
        System.out.println("调用了equals方法,当前对象【" + toString() + "】被比较对象【" + p.toString() + "】");
19
        return this.name.equals(((Person) p).name);
20
    }
21
22
    @Override
23
    public String toString() {
24
        return "{" + name + "," + age + "}";
25
    }
26
}
HashSet
HashSet<Person> set = new HashSet<>();
System.out.println("添加第1个元素");
set.add(new Person("a", 20));
System.out.println("添加第2个元素");
set.add(new Person("a", 21));// hashCode 不相同,直接判断为不同元素,所以直接添加进去
System.out.println("添加第3个元素");
set.add(new Person("b", 21)); // hashCode 相同,进一步判断 equals 方法是否相同;因为不相同,所以添加进去
System.out.println("添加第4个元素");
set.add(new Person("c", 21)); // hashCode 相同,进一步判断 equals 方法是否相同;因为不相同,所以添加进去
System.out.println("添加第5个元素");
set.add(new Person("b", 21)); // hashCode 相同,进一步判断 equals 方法是否相同;因为相同,所以不添加进去
System.out.println(set); //[{a,20}, {a,21}, {b,21}, {c,21}]
x
1
HashSet<Person> set = new HashSet<>();
2
System.out.println("添加第1个元素");
3
set.add(new Person("a", 20));
4
System.out.println("添加第2个元素");
5
set.add(new Person("a", 21));// hashCode 不相同,直接判断为不同元素,所以直接添加进去
6
System.out.println("添加第3个元素");
7
set.add(new Person("b", 21)); // hashCode 相同,进一步判断 equals 方法是否相同;因为不相同,所以添加进去
8
System.out.println("添加第4个元素");
9
set.add(new Person("c", 21)); // hashCode 相同,进一步判断 equals 方法是否相同;因为不相同,所以添加进去
10
System.out.println("添加第5个元素");
11
set.add(new Person("b", 21)); // hashCode 相同,进一步判断 equals 方法是否相同;因为相同,所以不添加进去
12
System.out.println(set); //[{a,20}, {a,21}, {b,21}, {c,21}]
HashMap
HashMap<Person, Integer> map = new HashMap<>();
System.out.println("添加第1个元素");
map.put(new Person("a", 20), 1);
System.out.println("添加第2个元素");
map.put(new Person("a", 21), 1);// hashCode 不相同,直接判断为不同元素,所以直接添加进去
System.out.println("添加第3个元素");
map.put(new Person("b", 21), 1); // hashCode 相同,进一步判断 equals 方法是否相同;因为不相同,所以添加进去
System.out.println("添加第4个元素");
map.put(new Person("c", 21), 1); // hashCode 相同,进一步判断 equals 方法是否相同;因为不相同,所以添加进去
System.out.println("添加第5个元素");
map.put(new Person("b", 21), 1); // hashCode 相同,进一步判断 equals 方法是否相同;因为相同,所以不添加进去
System.out.println(map.keySet()); //[{a,20}, {a,21}, {b,21}, {c,21}]
x
1
HashMap<Person, Integer> map = new HashMap<>();
2
System.out.println("添加第1个元素");
3
map.put(new Person("a", 20), 1);
4
System.out.println("添加第2个元素");
5
map.put(new Person("a", 21), 1);// hashCode 不相同,直接判断为不同元素,所以直接添加进去
6
System.out.println("添加第3个元素");
7
map.put(new Person("b", 21), 1); // hashCode 相同,进一步判断 equals 方法是否相同;因为不相同,所以添加进去
8
System.out.println("添加第4个元素");
9
map.put(new Person("c", 21), 1); // hashCode 相同,进一步判断 equals 方法是否相同;因为不相同,所以添加进去
10
System.out.println("添加第5个元素");
11
map.put(new Person("b", 21), 1); // hashCode 相同,进一步判断 equals 方法是否相同;因为相同,所以不添加进去
12
System.out.println(map.keySet()); //[{a,20}, {a,21}, {b,21}, {c,21}]
以上两种方式具有完全相同的日志:
添加第1个元素
调用了hashCode方法,当前对象【{a,20}】
添加第2个元素
调用了hashCode方法,当前对象【{a,21}】
添加第3个元素
调用了hashCode方法,当前对象【{b,21}】
调用了equals方法,当前对象【{b,21}】被比较对象【{a,21}】
添加第4个元素
调用了hashCode方法,当前对象【{c,21}】
调用了equals方法,当前对象【{c,21}】被比较对象【{a,21}】//首先是和第一个具有相同 hashCode 的元素比较
调用了equals方法,当前对象【{c,21}】被比较对象【{b,21}】//不一致时再和下一个具有相同 hashCode 的元素比较
添加第5个元素
调用了hashCode方法,当前对象【{b,21}】
调用了equals方法,当前对象【{b,21}】被比较对象【{a,21}】//首先是和第一个具有相同 hashCode 的元素比较
调用了equals方法,当前对象【{b,21}】被比较对象【{b,21}】//一旦发现具有相同 hashCode 的元素也相互 equals,就会立即结束
[{a,20}, {a,21}, {b,21}, {c,21}]
x
1
添加第1个元素
2
调用了hashCode方法,当前对象【{a,20}】
3
添加第2个元素
4
调用了hashCode方法,当前对象【{a,21}】
5
添加第3个元素
6
调用了hashCode方法,当前对象【{b,21}】
7
调用了equals方法,当前对象【{b,21}】被比较对象【{a,21}】
8
添加第4个元素
9
调用了hashCode方法,当前对象【{c,21}】
10
调用了equals方法,当前对象【{c,21}】被比较对象【{a,21}】//首先是和第一个具有相同 hashCode 的元素比较
11
调用了equals方法,当前对象【{c,21}】被比较对象【{b,21}】//不一致时再和下一个具有相同 hashCode 的元素比较
12
添加第5个元素
13
调用了hashCode方法,当前对象【{b,21}】
14
调用了equals方法,当前对象【{b,21}】被比较对象【{a,21}】//首先是和第一个具有相同 hashCode 的元素比较
15
调用了equals方法,当前对象【{b,21}】被比较对象【{b,21}】//一旦发现具有相同 hashCode 的元素也相互 equals,就会立即结束
16
[{a,20}, {a,21}, {b,21}, {c,21}]
2018-7-13




集合 源码分析判断是否是同一元素的策略

标签:swa   als   1.2   private   --   内容   ceo   not   lin   

原文地址:https://www.cnblogs.com/baiqiantao/p/9306725.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!