Java容器类类库的用途是"保存对象",并将其划分为两个不同的概念:
1) Collection
一组"对立"的元素,通常这些元素都服从某种规则
1.1) List:有序可重复
1.2) Set:无序不可重复
1.3) Queue保持一个队列(先进先出)的顺序
2) Map
一组成对的"键值对"对象
1. Set
Set集合中对象不可重复,因此当试图添加两个相同元素时,add()方法返回false/元素不可加入;
Set集合中对象必须重写hashCode()方法和equals()方法;
1.1 HashSet
按照Hash算法存储结合元素—>具有良好的存取/查找性能;
- 添加元素时,调用hashCode()方法计算对象的hashCode值;
- 根据hashCode值决定对象的存储位置—>元素存储顺序和添加顺序可能不一致(LinkedHashSet可保证);
- HashSet判断元素相等的标准:equal()方法相等/hashCode()方法返回值相同;
- Hash算法/哈希/散列:通过计算元素的hashCode值获取元素的存储位置—>价值在于存取/查找速度快
与数组相比,HashSet索引不需要连续,可自由增加HashSet长度;
1.2 LinkedHashSet
HashSet的子类,LinkedHashSet也是根据元素的hashCode值决定其存储位置;
同时使用链表维护元素的添加次序—>性能略低于HashSet的性能;
1.3 TreeSet
TreeSet实现SortSet接口,
TreeSet采用红黑树(red-black tree)存储集合元素,排序规则支持自然排序/定制排序;
TreeSet存储对象必须实现Comparable接口;
1.4 测试
HashSet测试
public class HashSetTestCase{
static class A {
private String name;
public A(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
A other = (A)obj;
return name.equals(other.name);
}
@Override
public String toString() {
return "A [name=" + name + "]";
}
}
static class B {
private String name;
public B(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public String toString() {
return "B [name=" + name + "]";
}
}
static class C {
private String name;
public C(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
C other = (C)obj;
return name.equals(other.name);
}
@Override
public String toString() {
return "C [name=" + name + "]";
}
}
public static void main(String[] args) {
A a1 = new A("1");
A a2 = new A("1");
B b1 = new B("1");
B b2 = new B("1");
C c1 = new C("1");
C c2 = new C("1");
System.out.println(a1 == a2);
System.out.println(a1.equals(a2));
System.out.println(b1 == b2);
System.out.println(b1.equals(b2));
System.out.println(c1 == c2);
System.out.println(c1.equals(c2));
HashSet<A> A1 = new HashSet<A>();
A1.add(a1);
A1.add(a2);
HashSet<B> B1 = new HashSet<HashSetTestCase.B>();
B1.add(b1);
B1.add(b2);
HashSet<C> C1 = new HashSet<HashSetTestCase.C>();
C1.add(c1);
C1.add(c2);
System.out.println(A1);
System.out.println(B1);
System.out.println(C1);
}
}
输出结果:
false
true
false
false
false
true
[A [name=1], A [name=1]]
[B [name=1], B [name=1]]
[C [name=1]]
LinkedHashSet测试
public class LinkedHashSetTestCase {
public static void main(String[] args) {
Set<String> linkedHashSet = new LinkedHashSet<String>();
Set<String> hashSet = new HashSet<String>();
linkedHashSet.add("2");
linkedHashSet.add("3");
linkedHashSet.add("51");
linkedHashSet.add("1");
linkedHashSet.add("21");
linkedHashSet.add("6");
linkedHashSet.add("7");
hashSet.add("2");
hashSet.add("3");
hashSet.add("51");
hashSet.add("1");
hashSet.add("21");
hashSet.add("6");
hashSet.add("7");
Iterator<String> linkedHashSetIterator = linkedHashSet.iterator();
while(linkedHashSetIterator.hasNext()) {
System.out.print(linkedHashSetIterator.next());
System.out.print(";");
}
System.out.println();
System.out.println("==============");
Iterator<String> hashSetIterator = hashSet.iterator();
while(hashSetIterator.hasNext()) {
System.out.print(hashSetIterator.next());
System.out.print(";");
}
}
}
输出结果:
2;3;51;1;21;6;7;
==============
1;2;3;6;7;51;21;
TreeSet测试
public class TreeSetTestCase {
static class A implements Comparable<TreeSetTestCase.A>{
private String name;
public A(String name) {
super();
this.name = name;
}
@Override
public String toString() {
return "A [name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
A other = (A)obj;
return name.equals(other.name);
}
@Override
public int compareTo(A o) {
return name.compareTo(o.name);
}
}
public static void main(String[] args) {
A a1 = new A("12");
A a2 = new A("32");
A a3 = new A("22");
A a4 = new A("27");
A a5 = new A("33");
TreeSet<A> treeSet = new TreeSet<A>();
treeSet.add(a1);
treeSet.add(a2);
treeSet.add(a3);
treeSet.add(a4);
treeSet.add(a5);
System.out.println(treeSet);
}
}
输出结果
[A [name=12], A [name=22], A [name=27], A [name=32], A [name=33]]
Tree原理扩展:
代码如下:
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(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);// 如果后面的值与树中的值相等,直接取前边的值,重新放到树中,如compareTo中两个值相等,查询处理就只会有一个
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
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<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}