集合
特点
- 确定性:对任意对象都能判定其是否属于某一个集合
- 互异性:集合内的每一个元素都是不一样的,内容也差异
- 无序性:集合内的顺序无关
Java内的集合接口
- HashSet(基于散列函数的集合,无序,不支持同步)
- TreeSet(基于树结构的集合,可排序的,不支持同步)
- LinkedHashSet(基于散列函数和双向链表的集合,可排序的,不支持同步)
HashSet
- 基于HashMap实现的,可以容纳null元素
- add 添加一个元素
- chear 清除整个HashSet里元素
- contains 判定是否包含一个元素
- remove 删除一个元素
- size 大小
- retainAll 计算俩个集合的交集
HashSet的各种遍历方法和操作
public class HashSetTest {
public static void main(String[] args) {
HashSet<Integer> hs = new HashSet<Integer>();
hs.add(null);
hs.add(1000);
hs.add(20);
hs.add(3);
hs.add(40000);
hs.add(5000000);
hs.add(3); //3 重复
hs.add(null); //null重复
System.out.println(hs.size()); //6
if(!hs.contains(6))
{
hs.add(6);
}
System.out.println(hs.size()); //7
hs.remove(4);
System.out.println(hs.size()); //6
//hs.clear();
//System.out.println(hs.size()); //0
System.out.println("============for循环遍历==============");
for(Integer item : hs)
{
System.out.println(item);
}
System.out.println("============测试集合交集==============");
HashSet<String> set1 = new HashSet<String>();
HashSet<String> set2 = new HashSet<String>();
set1.add("a");
set1.add("b");
set1.add("c");
set2.add("c");
set2.add("d");
set2.add("e");
//交集
set1.retainAll(set2);
System.out.println("交集是 "+set1);
System.out.println("============测试多种遍历方法速度==============");
HashSet<Integer> hs2 = new HashSet<Integer>();
for(int i=0;i<100000;i++) {
hs2.add(i);
}
traverseByIterator(hs2);
traverseByFor(hs2);
}
public static void traverseByIterator(HashSet<Integer> hs)
{
long startTime = System.nanoTime();
System.out.println("============迭代器遍历==============");
Iterator<Integer> iter1 = hs.iterator();
while(iter1.hasNext()){
iter1.next();
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
public static void traverseByFor(HashSet<Integer> hs)
{
long startTime = System.nanoTime();
System.out.println("============for循环遍历==============");
for(Integer item : hs)
{
;
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
}
结果
6
7
7
============for循环遍历==============
null
40000
3
20
6
1000
5000000
============测试集合交集==============
交集是 [c]
============测试多种遍历方法速度==============
============迭代器遍历==============
6387900纳秒
============for循环遍历==============
4639500纳秒
HashSet判断重复
public class ObjectHashSetTest {
public static void main(String[] args) {
System.out.println("==========Cat HashSet ==============");
HashSet<Cat> hs = new HashSet<Cat>();
hs.add(new Cat(2));
hs.add(new Cat(1));
hs.add(new Cat(3));
hs.add(new Cat(5));
hs.add(new Cat(4));
hs.add(new Cat(4));
System.out.println(hs.size()); //6
System.out.println("==========Dog HashSet ==============");
HashSet<Dog> hs2 = new HashSet<Dog>();
hs2.add(new Dog(2));
hs2.add(new Dog(1));
hs2.add(new Dog(3));
hs2.add(new Dog(5));
hs2.add(new Dog(4));
hs2.add(new Dog(4));
System.out.println(hs2.size()); //5
}
class Cat
{
private int size;
public Cat(int size)
{
this.size = size;
}
}
class Dog {
private int size;
public Dog(int s) {
size = s;
}
public int getSize() {
return size;
}
public boolean equals(Object obj2) {
System.out.println("Dog equals()~~~~~~~~~~~");
if(0==size - ((Dog) obj2).getSize()) {
return true;
} else {
return false;
}
}
public int hashCode() {
System.out.println("Dog hashCode()~~~~~~~~~~~");
return size;
}
public String toString() {
System.out.print("Dog toString()~~~~~~~~~~~");
return size + "";
}
}
dog重写的hashCode和equals,toString方法
hashset和hashmap的联系
- HashSet初始化(构造方法)是采用HashMap实现的(不信看源码),也就是说hashset=hashmap的实例,HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的
- HashMap的key=HashSet的对象,HashMap的value=HashSet中定义的一个统一常量private static final Object PRESENT = new Object()。 也就是说,在hashset中,我们用add方法添加元素时,传给底层hashmap实例的还是键值对(两个对象),只不过key直接就是对象元素本身,而value则是一个object常量(这就是让我们把这个value忽略掉,当成只传了一个对象)
- hashset中的添加方法是add,而hashmap中的添加方法是put。 put()方法应用于map集合中,add()方法应用于set集合中。 二者的主要区别是:返回值类型不一样。add()放回布尔(boolean)类型。 因为像Set集合中不允许添加重复的元素。当HashSet调用add()方法时,如果返回false,表示添加不成功。put()的使用是:添加时出现相同的键,那么后添加的值会替换(覆盖)掉此键对应的原来的值。并返回此键对应的原来的值。
LinkedHashSet
- 继承HashSet,也是基于HashMap实现的,可以容纳null元素
- 不支持同步
- 方法和HashSet 基本一致
- 通过一个双向链表来维护插入顺序(输出的顺序和插入顺序一样)
LinkedHashSet 遍历方法和操作
public class LinkedHashSetTest {
public static void main(String[] args) {
LinkedHashSet<Integer> lhs = new LinkedHashSet<Integer>();
lhs.add(null);
lhs.add(1000);
lhs.add(20);
lhs.add(3);
lhs.add(40000);
lhs.add(5000000);
lhs.add(3); //3 重复
lhs.add(null); //null 重复
System.out.println(lhs.size()); //6
if(!lhs.contains(6))
{
lhs.add(6);
}
System.out.println(lhs.size()); //7
lhs.remove(4);
System.out.println(lhs.size()); //6
//lhs.clear();
//System.out.println(lhs.size()); //0
System.out.println("============for循环遍历==============");
for(Integer item : lhs)
{
System.out.println(item);
}
LinkedHashSet<Integer> lhs2 = new LinkedHashSet<Integer>();
for(int i=0;i<100000;i++)
{
lhs2.add(i);
}
traverseByIterator(lhs2);
traverseByFor(lhs2);
}
public static void traverseByIterator(LinkedHashSet<Integer> hs)
{
long startTime = System.nanoTime();
System.out.println("============迭代器遍历==============");
Iterator<Integer> iter1 = hs.iterator();
while(iter1.hasNext()){
iter1.next();
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
public static void traverseByFor(LinkedHashSet<Integer> hs)
{
long startTime = System.nanoTime();
System.out.println("============for循环遍历==============");
for(Integer item : hs)
{
;
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
}
结果
6
7
7
============for循环遍历==============
null
1000
20
3
40000
5000000
6
============迭代器遍历==============
6292200纳秒
============for循环遍历==============
3483300纳秒
判断元素是否重复和HashSet一样
TreeSet
- 基于TreeMap 实现的**,不可以容纳null元素**。不支持同步
- 方法基本差不多
- 根据comparaTo方法或指定Comparator排序
- 元素需要继承自Comparable接口
TreeSet遍历和操作
public class TreeSetTest {
public static void main(String[] args) {
TreeSet<Integer> ts = new TreeSet<Integer>();
// ts.add(null); 错误,不支持null
ts.add(1000);
ts.add(20);
ts.add(3);
ts.add(40000);
ts.add(5000000);
ts.add(3); //3 重复
System.out.println(ts.size()); //5
if(!ts.contains(6))
{
ts.add(6);
}
System.out.println(ts.size()); //6
ts.remove(4);
System.out.println(ts.size()); //5
//lhs.clear();
//System.out.println(lhs.size()); //0
System.out.println("============for循环遍历==============");
for(Integer item : ts)
{
System.out.println(item);
}
TreeSet<Integer> ts2 = new TreeSet<Integer>();
for(int i=0;i<100000;i++)
{
ts2.add(i);
}
traverseByIterator(ts2);
traverseByFor(ts2);
}
public static void traverseByIterator(TreeSet<Integer> hs)
{
long startTime = System.nanoTime();
System.out.println("============迭代器遍历==============");
Iterator<Integer> iter1 = hs.iterator();
while(iter1.hasNext()){
iter1.next();
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
public static void traverseByFor(TreeSet<Integer> hs)
{
long startTime = System.nanoTime();
System.out.println("============for循环遍历==============");
for(Integer item : hs)
{
;
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println(duration + "纳秒");
}
}
结果
5
6
6
============for循环遍历==============
3
6
20
1000
40000
5000000
============迭代器遍历==============
6431600纳秒
============for循环遍历==============
5674100纳秒
判断元素是否重复
public class ObjectTreeSetTest {
public static void main(String[] args) {
//添加到TreeSet的,需要实现Comparable接口,即实现compareTo方法
System.out.println("==========Tiger TreeSet ==============");
TreeSet<Tiger> ts3 = new TreeSet<Tiger>();
ts3.add(new Tiger(2));
ts3.add(new Tiger(1));
ts3.add(new Tiger(3));
ts3.add(new Tiger(5));
ts3.add(new Tiger(4));
ts3.add(new Tiger(4));
System.out.println(ts3.size()); //5
}
}
public class Tiger implements Comparable{
private int size;
public Tiger(int s) {
size = s;
}
public int getSize() {
return size;
}
public int compareTo(Object o) {
System.out.println("Tiger compareTo()~~~~~~~~~~~");
return size - ((Tiger) o).getSize();
}
}
总结
- HashSet,LinkedHashSet,TreeSet的元素都只能是对象
- HashSet,LinkedHashSet判断元素重复的原则
– 判断俩个元素的hashCode方法返回值是否相同,若不同,返回false
– 若两者hashCode相同,这判断equals方法,若不同,返回false,否则返回true
– hashCode和equals方法是所有的类都有,因为Object类有
–需要重写hashCode方法,因为本身继承来的方法是会返回对象信息和内存地址经过运算后的一个int值,俩个地址不一样,里面值一样的元素会判定为不一样的,所以要重写
–hashCode方法和equals方法,和toString,通常这三个方法是一起重写的 - TreeSet 判定元素重复原则
–元素需要继承自Comparable接口
–比较俩个元素的comparaTo方法