JAVA核心技术I---JAVA基础知识(集合set)
一:集合了解
(一)确定性,互异性,无序性
确定性:对任意对象都能判定其是否属于某一个集合
互异性:集合内每个元素都是无差异的,注意是内容差异
无序性:集合内的顺序无关
(二)集合接口HashSet,TreeSet,LinkedHashSet
–HashSet (基于散列函数的集合,无序,不支持同步)
–TreeSet (基于树结构的集合,可排序的,不支持同步)
–LinkedHashSet(基于散列函数和双向链表的集合,可排序的,不支持同步
二:HashSet
(一)基础方法
–基于HashMap实现的,可以容纳null元素, 不支持同步 Set s = Collections.synchronizedSet(new HashSet(...)); –add 添加一个元素 –clear 清除整个HashSet –contains 判定是否包含一个元素 –remove 删除一个元素 size 大小 –retainAll 计算两个集合交集
(二)HashSet实现
HashSet<Integer> hs = new HashSet<Integer>(); //<>是泛型编程,类似于C++模板 hs.add(null); hs.add(10000); hs.add(22); hs.add(1010); hs.add(50001010); hs.add(101035); hs.add(3); System.out.println(hs.size()); if(!hs.contains(6)) { hs.add(6); } System.out.println(hs.size()); hs.remove(4); //存在,则删除,不存在,则不操作 for(Integer item : hs) { System.out.println(item); }
7 8 null //无序性 10000 1010 3 22 6
50001010
101035
(三)性能测试:因为无序性,无索引操作。for效率高
public static void trverseByIterator(HashSet<Integer> hs) { //使用迭代器遍历 System.out.println("==========迭代器遍历==========="); long startTime = System.nanoTime(); //获取开始时间,以纳秒为单位返回正在运行的Java虚拟机的高分辨率时间源的当前值。 Iterator<Integer> iter = hs.iterator(); //获取迭代指针 while(iter.hasNext()) { iter.next(); } long endTime = System.nanoTime(); long duration = endTime-startTime; System.out.println("iterator使用纳秒:"+duration); } public static void trverseByFor(HashSet<Integer> hs) { //使用迭代器遍历 System.out.println("==========for索引遍历==========="); long startTime = System.nanoTime(); //获取开始时间,以纳秒为单位返回正在运行的Java虚拟机的高分辨率时间源的当前值。 for(Integer item : hs) { ; } long endTime = System.nanoTime(); //获取开始时间,以纳秒为单位返回正在运行的Java虚拟机的高分辨率时间源的当前值。 long duration = endTime-startTime; System.out.println("for使用纳秒:"+duration); }
==========迭代器遍历=========== iterator使用纳秒:5738665 ==========for索引遍历=========== for使用纳秒:2721950
(四)retainAll交集测试
//测试交集 HashSet<String> hs1 = new HashSet<String>(); HashSet<String> hs2 = new HashSet<String>(); hs1.add("a"); hs1.add("b"); hs1.add("c"); hs2.add("c"); hs2.add("d"); hs2.add("e"); hs1.retainAll(hs2); //将交集保存在hs1中 for(String item : hs1) { System.out.println(item); }
c
三:LinkedHashSet(与HashSet一致)
–继承HashSet,也是基于HashMap实现的,可以容纳null元素,按照插入顺序有序 –不支持同步 Set s = Collections.synchronizedSet(new LinkedHashSet(...)); –方法和HashSet基本一致 add, clear, contains, remove, size –通过一个双向链表维护插入顺序
四:TreeSet
(一)基本方法
–基于TreeMap实现的,不可以容纳null元素,不支持同步 SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...)); –add 添加一个元素 –clear 清除整个TreeSe –contains 判定是否包含一个元素 –remove 删除一个元素 size 大小 –根据compareTo方法或指定Comparator排序
(二)实现(有序,会自动排序,红黑树)
TreeSet<Integer> ts = new TreeSet<Integer>(); //<>是泛型编程,类似于C++模板 ts.add(1000); ts.add(15300); ts.add(100); ts.add(3); ts.add(566000); if(!ts.contains(4)) { ts.add(4); }
for(Integer item : ts) { System.out.println(item);; }
4 100 1000 15300 566000
(三)性能测试:for更加高效
==========迭代器遍历=========== iterator使用纳秒:9246423 ==========for索引遍历=========== for使用纳秒:3366874
五:HashSet, LinkedHashSet, TreeSet对象比较(元素重复)《重点》
(一)HashSet和LinkedHashSet判定元素重复的原则
–判定两个元素的hashCode返回值是否相同,若不同,返回false
–若两者hashCode相同,判定equals方法,若不同,返回false;否则返回true。
–hashCode和equals方法是所有类都有的,因为Object类有
比较之前会先调用hashCode,之后是equals方法
1.正常执行,含重复
class Dog{ int age; public Dog(int a) { this.age=a; } } public class CompareTest { public static void main(String[] args) { Dog d1=new Dog(10); Dog d2=new Dog(10); HashSet<Dog> hs=new HashSet<Dog>(); hs.add(new Dog(10)); hs.add(new Dog(1)); hs.add(new Dog(3)); hs.add(new Dog(10)); hs.add(new Dog(10)); System.out.println(hs.size()); } }
5
Dog类本身没有hashCode方法,继承于Object,而Object类的hashCOde会返回对象信息和内存地址经过运算后的一个值。两个不同对象,其值必然不一致
2.实现对象的hashCode方法和equals方法实现去重
import java.util.*; class Dog{ int age; public Dog(int a) { this.age=a; } public int getAge() { return this.age; } public int hashCode() { System.out.println("hashCode exec..."); return this.age; } public boolean equals(Object obj2) { System.out.println("equals exec..."); if(0==this.age-((Dog)obj2).getAge()) return true; else return false; } } public class CompareTest { public static void main(String[] args) { Dog d1=new Dog(10); Dog d2=new Dog(10); HashSet<Dog> hs=new HashSet<Dog>(); hs.add(new Dog(10)); hs.add(new Dog(1)); hs.add(new Dog(3)); hs.add(new Dog(10)); hs.add(new Dog(10)); System.out.println(hs.size()); } }
hashCode exec... hashCode exec... hashCode exec... hashCode exec... equals exec... hashCode exec... equals exec... 3 //去重实现
先执行hashCode,只有hashCode通过,才会执行equals方法
public String toString() { System.out.println("toString exec..."); return age+""; }
要保持equals,hashCode和toString三位一体。都应该各自相同
(二) TreeSet去重
添加到TreeSet,需要实现Comparable接口,即实现compareTo方法
与hashCode和equals无关,只与compareTo有关
import java.util.*; class Dog implements Comparable{ int age; public Dog(int a) { this.age=a; } public int getAge() { return this.age; } public int hashCode() { System.out.println("hashCode exec..."); return this.age; } public boolean equals(Object obj2) { System.out.println("equals exec..."); if(0==this.age-((Dog)obj2).getAge()) return true; else return false; } public String toString() { System.out.println("toString exec..."); return age+""; } public int compareTo(Object obj2) { System.out.println("compareTo exec..."); return this.age - ((Dog)obj2).getAge(); } } public class CompareTest { public static void main(String[] args) { Dog d1=new Dog(10); Dog d2=new Dog(10); TreeSet<Dog> hs=new TreeSet<Dog>(); hs.add(new Dog(10)); hs.add(new Dog(1)); hs.add(new Dog(3)); hs.add(new Dog(10)); hs.add(new Dog(10)); System.out.println(hs.size()); } }
compareTo exec... compareTo exec... compareTo exec... compareTo exec... compareTo exec... compareTo exec... compareTo exec... compareTo exec... 3
可以知道,去重和hashCode与equals无关,不执行。而是直接去找compareTo方法
六:总结
(一)HashSet, LinkedHashSet, TreeSet的元素都只能是对象
会进行自动装箱
(二)HashSet和LinkedHashSet判定元素重复的原则《重点》
–判定两个元素的hashCode返回值是否相同,若不同,返回false
–若两者hashCode相同,判定equals方法,若不同,返回false;否则返回true。
–hashCode和equals方法是所有类都有的,因为Object类有
(三)TreeSet判定元素重复的原则《重点》
–需要元素继承自Comparable接口
–比较两个元素的compareTo方法
(四)注意:对于基本类型的包装类。本来就实现了compareTo接口和其他比较方法,所以HashSet,LinkedHashSet,TreeSet中对于包装类是默认去重的