Set集合:一个不包含重复元素的Collection集合,元素不重复,List集合是允许元素重复的。
Set接口的三个字类:HashSet(),LinkedHashSet(),TreeSet()
HashSet()集合底层使用的是hashMap()来存储元素,底层数据结构是哈希表,元素无序,且不允许元素重复(存的顺序和去的顺序不一致),可以存储null元素,线程不安全,效率高。哈希表:是一个元素为链表的数组,综合了数组和链表的优点(JDK1.7之前),(JDK1.8之后优化了数组+链表+二叉树)
HashSet(),构造一个新的容器空set,其底层是HashMap实例,默认初始容量是16,加载因子是0.75。
import java.util.HashSet;
public class Mytest {
public static void main(String[] args) {
/*
* Set接口:一个不包含重复元素的Collection,元素不重复。
* list:允许元素重复
* Set接口的三个字类:HashSet,LinkedHashSet,TreeSet
* HashSet:底层数据结构是哈希表,元素无序(存的顺序和取的顺序不一致),
* 且不允许元素重复,可以存储null元素,线程不安全的,但是效率高
*/
//HashSet(),构造一个新的空set,其底层HashMap实例的默认初始容量是16,加载因子是0.75
HashSet hashSet = new HashSet<>();
hashSet.add("王俊凯");
hashSet.add("王源");
hashSet.add("易烊千玺");
hashSet.add("王俊凯");
hashSet.add("王源");
hashSet.add("易烊千玺");
hashSet.add("王俊凯");
hashSet.add("王源");
hashSet.add("易烊千玺");
System.out.println(hashSet);
System.out.println("---------------------");
for (String s : hashSet) {
System.out.println(s);
}
}
}
当我们往HashSet集合中存储对象时,会调用hashCode()方法,算出一个值,这个值就是确定这个对象放到表中的位置。那假如有两个对象,算出的位置是一样的,就会调用equals()方法,去比较两个对象的地址值是否一样,如果不一样那就存储进去,形成链表结构。
import java.util.HashSet;
public class Mytest {
public static void main(String[] args) {
/*
*HashSet底层数据结构是哈希表,HashSet是线程不安全的,集合元素可以是null
*哈希表:是一个元素为链表的数组,综合了数组和集合的所有优点(像新华字典一样)
* (JDK1.7之前)JDK1.8之后优化了(数组+链表+二叉树)
*/
//存储元素为student类型,无序且不允许元素重复
HashSet hashSet = new HashSet<>();
hashSet.add(new Student("夏雪",25));
hashSet.add(new Student("刘星",24));
hashSet.add(new Student("夏雨",23));
hashSet.add(new Student("夏雪",25));
hashSet.add(new Student("刘星",24));
hashSet.add(new Student("夏雨",23));
for (Student student : hashSet) {
System.out.println(student);
}
/*当我们往hashset集合中存储对象时,会调用对象的hashcode方法,得到一个值,
这个值就是确定这个对象放到表中的位置*/
/*那如果我们有两个对象算出来的位置值是一样的,就会调用equals()方法,去比较两个对象的地址值是否相同,
如果地址值不相同,那就存储进去形成链表结构。*/
}
}
---------------------------------
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
System.out.println("equals调用了 this:"+this+"==="+"之前的元素"+o);
//由于调用equals方法,碰撞次数过多,因此我们去比较 他们的成员变量值
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
//return super.equals(o);
}
@Override
public int hashCode() {
//return Objects.hash(name, age);
//如果写死hashCode值,肯定会造成碰撞次数过多
return 0;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
为了减少碰撞,我们应该合理的去重写hashCode方法,来减少碰撞(减少调用equals方法的次数),尽量是在哈希表中横向排列,减少链表的形成。
public class Mytest2 {
public static void main(String[] args) {
HashSet hashSet = new HashSet<>();
hashSet.add(1000);
hashSet.add(2000);
hashSet.add(3000);
hashSet.add(1000);
hashSet.add(2000);
hashSet.add(3000);
hashSet.add(1000);
hashSet.add(2000);
hashSet.add(3000);
System.out.println(hashSet);
System.out.println("-----------------");
for (Integer integer : hashSet) {
System.out.println(integer);
}
//String类和Integer类,已经重写了hashCode()方法和equals()方法
}
}
HashSet集合能够保证元素唯一性的原因重写了hashCode()方法和equals()方法来保证的,如果元素不重写hashCode()方法和equals()方法,则无法保证元素的唯一性。
我们合理的重写hashCode()方法,是为了元素能够在哈希表中尽可能的横向排列,减少碰撞(减少equals方法的调用)
import org.westos.demo2.Student;
import java.util.HashSet;
public class Mytest {
public static void main(String[] args) {
/*
*HashSet底层数据结构是哈希表,HashSet是线程不安全的,集合元素可以是null
*哈希表:是一个元素为链表的数组,综合了数组和集合的所有优点(像新华字典一样)
* (JDK1.7之前)JDK1.8之后优化了(数组+链表+二叉树)
*/
//存储元素为student类型,无序且不允许元素重复
HashSet hashSet = new HashSet<>();
hashSet.add(new Student("夏雪",25));
hashSet.add(new Student("刘星",24));
hashSet.add(new Student("夏雨",23));
hashSet.add(new Student("夏雪",25));
hashSet.add(new Student("刘星",24));
hashSet.add(new Student("夏雨",23));
for (Student student : hashSet) {
System.out.println(student.getName() + "===" + student.getAge());
}
/*当我们往hashset集合中存储对象时,会调用对象的hashcode方法,得到一个值,
这个值就是确定这个对象放到表中的位置*/
/*那如果我们有两个对象算出来的位置值是一样的,就会调用equals()方法,去比较两个对象的地址值是否相同,
如果地址值不相同,那就存储进去形成链表结构。*/
/*hashSet:集合能保证元素的唯一性,是靠元素重写hashCode()方法和equals()方法来保证的。
如果元素不重写hashCode()和equals()方法,则无法保证元素的唯一性。
我们合理的重写hashCode()方法是为了元素能够在哈希表中尽量横线分布,减少碰撞(减少equals方法的调用)*/
}
}
----------------------------------
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/* @Override
public boolean equals(Object o) {
//System.out.println("equals调用了 this:"+this+"==="+"之前的元素"+o);
//由于调用equals方法,碰撞次数过多,因此我们去比较 他们的成员变量值
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
//return super.equals(o);
}
@Override
public int hashCode() {
//return Objects.hash(name, age);
//如果写死hashCode值,肯定会造成碰撞次数过多
return this.name.hashCode()+this.age*5;
}*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.util.HashSet;
import java.util.Iterator;
import java.util.function.Consumer;
public class Mytest {
public static void main(String[] args) {
HashSet hashSet = new HashSet<>();
hashSet.add(100);
hashSet.add(200);
hashSet.add(300);
//hashSet集合遍历的三种方式
//1.使用迭代器进行遍历
Iterator iterator = hashSet.iterator();
while (iterator.hasNext()) {
Integer next = iterator.next();
System.out.println(next);
}
System.out.println("-------------------");
//2.使用增强for循环来进行遍历
for (Integer integer : hashSet) {
System.out.println(integer);
}
System.out.println("-------------------");
//3.使用forEach()方法来进行遍历
hashSet.forEach(new Consumer() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
}
}
对元素进行去重:HashSet(Collection extends E> c),构造一个包含指定 collection 中的元素的新 set。你把List集合传进来,构建一个HashSet集合,就帮你去重了。
import java.util.ArrayList;
import java.util.HashSet;
public class Mytest2 {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add(100);
list.add(100);
list.add(100);
list.add(200);
list.add(200);
list.add(200);
list.add(300);
list.add(300);
list.add(300);
System.out.println(list);
/*对元素进行去重
HashSet(Collection extends E> c)
构造一个包含指定 collection 中的元素的新 set。
你把List集合传进来,构建一个HashSet集合,就帮你去重了*/
HashSet hashSet = new HashSet<>(list);
System.out.println(hashSet);
}
}
LinkedHashSet集合的底层数据结构是链表+哈希表,链表能够保证元素的有序,哈希表能够保证元素的唯一性。(但是还是要重写hashCode方法和equals方法),LinkedHashSet元素有序且唯一,线程不安全的,但效率高。
import java.util.LinkedHashSet;
public class Mytest {
public static void main(String[] args) {
/*
*LinkedHashSet 底层数据结构是链表+哈希表,
* 链表能够保证元素有序,哈希表能够保证元素唯一。(但是还是要重写hashCode()和equals()方法)
* LinkedHashSet 元素有序且唯一,线程不安全,但效率高
* hashSet:无序,唯一
*/
LinkedHashSet linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("唐僧");
linkedHashSet.add("孙悟空");
linkedHashSet.add("猪八戒");
linkedHashSet.add("沙悟净");
linkedHashSet.add("唐僧");
linkedHashSet.add("孙悟空");
linkedHashSet.add("猪八戒");
linkedHashSet.add("沙悟净");
//遍历元素
for (String s : linkedHashSet) {
System.out.println(s);
}
}
}
TreeSet:底层数据结构使用的是二叉树,元素唯一,且能对元素进行排序。
TreeSet集合对元素进行排序有两种方法:自然排序法和比较器排序法。
(1)自然排序法:当你使用空参构造时,就是自然排序法。如果使用自然排序法,自然排序对元素有要求,要求元素必须实现一个Comparable接口,重写这个接口中的compareTo方法,这个比较的方法是根据返回值是 0 正 负 来决定元素在二叉树中放的位置,以及是否往进放。
import java.util.TreeSet;
public class Mytest{
public static void main(String[] args) {
//使用自然排序:来排序学生对象,根据学生的年龄大小 来排序
TreeSet treeSet = new TreeSet<>();
treeSet.add(new Student("诸葛亮",50));
treeSet.add(new Student("周瑜",48));
treeSet.add(new Student("刘备",52));
treeSet.add(new Student("关羽",58));
treeSet.add(new Student("张飞",45));
treeSet.add(new Student("曹操",55));
treeSet.add(new Student("诸葛亮",50));
treeSet.add(new Student("关羽",58));
//如果这里出现了年龄一样,但是名字不一样的情况。这时候就需要我们在适当修改一下compareTo方法
treeSet.add(new Student("孙权",52));
//遍历元素
for (Student student : treeSet) {
System.out.println(student.getName()+"======"+student.getAge());
}
}
}
-------------------------------------------
public class Student implements Comparable{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Student o) {
//按照年龄的大小来比较
int num=this.age-o.age;
//年龄相同,但是名字不一样的情况,这种就需要比较姓名是否相同
//使用三目运算符进行判别上面这种情况
int num2=num==0 ? this.name.compareTo(o.name):num;
////返回值的 正 负 0 来决定元素在二叉树中的放置的左右位置,返回0 就不往里面存储
return num2;
//return 0;//返回是0,你写死了,只能返回一个根元素。
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
(2)比较器排序法: TreeSet(Comparator super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。Comparator接口,强行对某个对象Collections进行了整体排序比较函数。采用比较器排序,根据比较器中的compare()方法的返回值的正 负 0 ,来决定元素在二叉树中放的位置。
import java.util.Comparator;
import java.util.TreeSet;
public class Mytest {
public static void main(String[] args) {
//使用比较器排序,按照姓名的长度来排序
TreeSet treeSet = new TreeSet<>(new Comparator() {
@Override
public int compare(Student s1, Student s2) {
//先比较姓名的长度
int num1 = s1.getName().length() - s2.getName().length();
//姓名长度一样时,再比较年龄大小
int num2=num1==0?s1.getAge()-s2.getAge():num1;
//年龄相同时,再比较姓名的内容
int num3=num2==0?s1.getName().compareTo(s2.getName()):num2;
return num3;
}
});
treeSet.add(new Student("吴用智多星",50));
treeSet.add(new Student("黑旋风李逵", 57));
treeSet.add(new Student("宋江及时雨", 57));
treeSet.add(new Student("武松行者", 57));
treeSet.add(new Student("花和尚鲁智深", 23));
treeSet.add(new Student("立地太岁阮小二", 97));
treeSet.add(new Student("扈三娘", 47));
treeSet.add(new Student("白胜", 17));
treeSet.add(new Student("时迁", 17));
treeSet.add(new Student("西门大官人", 87));
for (Student student : treeSet) {
System.out.println(student.getName()+"==="+student.getAge());
}
}
}
----------------------------------------
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class Mytest2 {
public static void main(String[] args) {
//Comparator 这个比较器,不光TreeSet能用,其他的有些类也能用
//可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort)
ArrayList list = new ArrayList<>();
list.add(1000);
list.add(800);
list.add(950);
list.add(860);
list.add(503);
list.add(640);
list.add(999);
list.sort(new Comparator() {
@Override
public int compare(Integer a, Integer b) {
return a-b;
}
});
System.out.println(list);
System.out.println("------------------");
int[] arr={20,30,90,41,33};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
System.out.println("------------------");
Integer[] arr2={20,30,1,-5,90,41,33};
//参数2:可以传入比较器
Arrays.sort(arr2, new Comparator() {
@Override
public int compare(Integer a, Integer b) {
return b-a;
}
});
System.out.println(Arrays.toString(arr2));
}
}