集合
Set集合
Set集合概述和特点
- 特点:
- 不能有重复元素
- 没有带索引的方法,不能使用普通for循环遍历
//HashSet:对集合的迭代顺序不做任何保证
public static void main(String[] args) {
//创建Set集合对象
Set<String> set=new HashSet<>();
//添加元素
set.add("Hello");
set.add("world");
set.add("Set");
set.add("Hello");
//遍历集合
for (String str : set) {
System.out.print(str+"\t");//world Set Hello 1、输出顺序和存储顺序不一致
// 2、不能存储相同元素
}
}
哈希值
- 定义:哈希值是JDK根据对象的地址或字符串或数字算出来的int类型的数值
- Object中有获取哈希值的方法:.hashCode()
- 对象的哈希值特点:
- 同一个对象多次调用hashCode()获得的值相同,但哈希值相同对象不一定相同
- 默认情况下,不同对象的哈希值是不同的,但可以通过方法重写可以实现不同对象的哈希值相同
public static void main(String[] args) {
Student student1=new Student(1,"张三",18);
Student student2=new Student(2,"李四",19);
Student student3=new Student(3,"王五",20);
int i1 = student1.hashCode();
int i11 = student1.hashCode();
int i2 = student2.hashCode();
int i3 = student3.hashCode();
System.out.println(i1);//21685669
//同一个对象多次调用hashCode()获得的值相同,但哈希值相同对象不一定相同
System.out.println(i11);//21685669
//默认情况下,不同对象的哈希值是不同的
//通过方法重写可以实现不同对象的哈希值相同
System.out.println(i2);//2133927002
System.out.println(i3);//1836019240
}
哈希表
- JDK8之前,s底层采用数组+链表实现,可以说是一个元素为链表的数组
- JDK8之后,在长度比较长的时候,底层实现了优化
- 过程:
- 1、定义数组(长度为16)
- 2、先计算要存储的元素的哈希值;
- 3、根据哈希值取余得到的值为数组下标值
- 4、依次存储数据,如果该下标没有存储值,怎把元素直接存储进去;
如果该下标已经存储了元素,用该元素的哈希值依次和已存储元素对比,
如果哈希值不同,存储该元素
如果哈希值存在哈希值与该元素哈希值相同,则依次比较内容,
如果存在内容与该元素类容相同的情况,不存储
如果内容都不相同,存储
HashSet集合概述和特点
- 底层数据结构是哈希表
- 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出元素顺序一致
- 没有带索引的方法,不能使用普通for循环遍历
- 由于是Set集合,不包含重复元素的集合
//HashSet集合
public static void main(String[] args) {
HashSet<String> hashSet=new HashSet<>();
hashSet.add("Hello");
hashSet.add("World");
hashSet.add("HashSet");
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next()+"\t"); //Hello World HashSet
}
}
HashSet保证元素唯一性
- HashSet添加元素过程
- HashSet集合存储元素需要保证元素的唯一性,需要重新hashCode()和equals()
//HashSet集合
public static void main(String[] args) {
HashSet<String> hashSet=new HashSet<>();
hashSet.add("Hello");
hashSet.add("World");
hashSet.add("HashSet");
hashSet.add("World");
/**
* 1、 public boolean add(E e) {
* return map.put(e, PRESENT)==null;
* }
* 2、public V put(K key, V value) {
* return putVal(hash(key), key, value, false, true);
* }
* 3-1、static final int hash(Object key) {
* int h;
* return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
* }
* //hash的值和hashCode方法相关
* 3-2、final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
* boolean evict) {
* Node<K,V>[] tab; Node<K,V> p; int n, i;
* //如果哈希表未初始化,就对其进行初始化
* if ((tab = table) == null || (n = tab.length) == 0)
* n = (tab = resize()).length;
* //根据对象的哈希值计算对象的存储位置,如果该位置为null(没有元素),就存储元素
* if ((p = tab[i = (n - 1) & hash]) == null)
* tab[i] = newNode(hash, key, value, null);
* else {
* Node<K,V> e; K k;
* //存入的元素和以前的元素比较哈希值,
* 如果哈希值不同,会继续向下执行,把元素添加到集合
* 如果哈希值相同,会调用对象的equals方法比较 ,
* 如果返回false会继续向下执行,把元素添加到集合
* 如果equals返回true,说明元素重复,不存储
* if (p.hash == hash &&
* ((k = p.key) == key || (key != null && key.equals(k))))
* e = p;
* else if (p instanceof TreeNode)
* e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
* else {
* for (int binCount = 0; ; ++binCount) {
* if ((e = p.next) == null) {
* p.next = newNode(hash, key, value, null);
* if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
* treeifyBin(tab, hash);
* break;
* }
* if (e.hash == hash &&
* ((k = e.key) == key || (key != null && key.equals(k))))
* break;
* p = e;
* }
* }
* if (e != null) { // existing mapping for key
* V oldValue = e.value;
* if (!onlyIfAbsent || oldValue == null)
* e.value = value;
* afterNodeAccess(e);
* return oldValue;
* }
* }
* ++modCount;
* if (++size > threshold)
* resize();
* afterNodeInsertion(evict);
* return null;
* }
*/
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next()+"\t"); //Hello World HashSet
}
}
HashSet添加元素
public static void main(String[] args) {
HashSet<Student> hashSet=new HashSet<>();
Student student1=new Student(1,"张三",25);
Student student2=new Student(2,"李四",35);
Student student3=new Student(3,"王五",20);
Student student4=new Student(1,"张三",25);
System.out.println(student1.hashCode());//21685669
System.out.println(student4.hashCode());//2133927002
hashSet.add(student1);
hashSet.add(student2);
hashSet.add(student3);
hashSet.add(student4);
for (Student student : hashSet) {
System.out.println(student);//Student{id=3, name='王五', age=20}
//Student{id=2, name='李四', age=35}
//Student{id=1, name='张三', age=25}
//Student{id=1, name='张三', age=25}
}
//问题:张三被输出了两次
//重新Student的hashCode和equals方法
}
-------------------------------------------------------------
@Override
public int hashCode() {
return 0;
}
@Override
public boolean equals(Object obj) {
if(this==obj)
return true;
if(obj==null||getClass()!=obj.getClass())
return false;
Student student= (Student) obj;
if(student.getId()==getId()&&student.getAge()==getAge()&&student.getId()==getId())
return true;
return false;
}
LinkedHashSet集合概述和特点
- 特点:
- 由哈希表+链表实现的Set接口,具有可预测的迭代次序
- 由链表保证元素有序,也就是说元素的存储和取出顺序是一致一致
- 有哈希表保证元素唯一,也就是说没有重复的元素
//LinkedHashSet集合
public static void main(String[] args) {
LinkedHashSet<String> linkedHashSet=new LinkedHashSet<>();
linkedHashSet.add("张三");
linkedHashSet.add("李四");
linkedHashSet.add("张三");
linkedHashSet.add("王五");
for (String str : linkedHashSet) {
System.out.print(str+"\t");//张三 李四 王五 特点: 1、有序
// 2、唯一,不可重复
}
}
TreeSet集合概述和特点
- 元素有序,这里的有序不是指存储顺序和取出的顺序一致,而是按照一定的规则进行排序,具体的排序方式取决于构造器方法
- 无参构造方法TreeSet() :根据其元素的自然排序进行排序
- TreeSet(Comparator comparator) : 根据指定的比较器进行排序 - 没有带索引的方法,不能使用普通的for循环遍历
- 由于是Set集合,所以是不包含重复的元素的集合
自然排序Comparable - 用TreeSet集合存储自定义对象,无参构造方法使用自然排序对元素进行排序,让元素所属的类实现Comparable接口,并重新compareTo()方法
- 重写compareTo()方法时一定要注意排序规则必须要按照要求的主要条件和次要条件来写
- compareTo()返回值为0是不添加元素,利用这点可以保证集合元素的不可重复性
public class SetDemo07 {
//TreeSet中的自然排序Comparable
//要求:按照年龄排序,年龄相同按照姓名排序
public static void main(String[] args) {
TreeSet<Person> treeSet=new TreeSet();
Person person1=new Person("zs",1,19);
Person person2=new Person("ls",2,18);
Person person3=new Person("ww",3,20);
Person person4=new Person("zs",1,19);
Person person5=new Person("xx",1,19);
treeSet.add(person1);
treeSet.add(person2);
treeSet.add(person3);
treeSet.add(person4);
treeSet.add(person5);
for (Person person : treeSet) {
System.out.println(person.getId()+","+person.getName()+","+person.getAge());
//2,ls,18 1,xx,19 1,zs,19 3,ww,20
}
}
}
class Person implements Comparable<Person>{
private String name;
private int id;
private int age;
public Person(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Person o) {
//>0 升序
//<0 降序
//==0 不添加,保证了不可重复唯一性
int num = this.getAge() - o.getAge();
if(num==0) {
return this.getName().compareTo(o.getName());
}
return num;
}
}
比较器排序Comparator
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序,就是让集合构造方法接收Comparator的实现类对象,重新compare(Object o1, Object o2) 方法
- 重写compare()方法时一定要注意排序规则必须要按照要求的主要条件和次要条件来写
- compare()返回值为0是不添加元素,利用这点可以保证集合元素的不可重复性
//TreeSet中的比较器排序Comparator
public class Demo08 {
public static void main(String[] args) {
TreeSet<Human> treeSet=new TreeSet<Human>(new Comparator<Human>() {
@Override
public int compare(Human o1, Human o2) {
int num=o1.getAge()-o2.getAge();
if (num==0){
return o1.getName().compareTo(o2.getName());
}
return num;
}
});
Human human1=new Human("zs",1,19);
Human human2=new Human("ls",2,18);
Human human3=new Human("ww",3,20);
Human human4=new Human("zs",1,19);
Human human5=new Human("xx",1,19);
treeSet.add(human1);
treeSet.add(human2);
treeSet.add(human3);
treeSet.add(human4);
treeSet.add(human5);
for (Human human : treeSet) {
System.out.println(human.getName()+","+human.getId()+","+human.getAge());
//ls,2,18 xx,1,19 zs,1,19 ww,3,20
}
}
}
class Human{
private String name;
private int id;
private int age;
public Human(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}