集合框架(二)

Set集合

Set集合是一个无序不重复的接口,里面有两个实现类HashSet和TreeSet

HashSet类

 创建HashSet的语法:

①HashSet 名称=new HashSet();
②HashSet set1=new HashSet(初始化容量数);
③HashSet set2=new HashSet(初始化容量数 ,负载因子);
负载因子:当数量到达容量的多少时进行扩容,默认75%。

举个例子:

 1 public class Test2 {
 2     public static void main(String[] args) {
 3         //创建一个无参的HashSet对象,
 4         HashSet set=new HashSet();
 5         //初始化容器的大小,初始化容量为12
 6         HashSet set1=new HashSet(12);
 7         //初始化容器的大小,并附上负载因子
 8         HashSet set2=new HashSet(12,0.7f);
 9         // (这个例子中)负载因子的概念是:当容量超过70%,进行扩容操作
10     }
11 }

添加操作

语法:

boolean  add(E e):将指定元素添加到此集合(如果尚未存在) 

举个例子:

1 public class Test2 {
2     public static void main(String[] args) {
3         //创建一个无参的HashSet对象,
4         HashSet set=new HashSet();
5         //添加一个尚未存在的元素时返回true,否则返回false
6         set.add(12);
7     }
8 }

删除操作

语法:

boolean  remove(Object o):删除指定的元素 

举个例子:

1 public class Test2 {
2     public static void main(String[] args) {
3         //创建一个无参的HashSet对象,
4         HashSet set=new HashSet();
5         //删除一个指定的元素
6         set.remove(12);
7     }
8 }

遍历操作

HashSet是一个无序不重复的结构,所以没有索引值只能通过迭代器增强for循环实现遍历

①迭代器

 1 public class Test2 {
 2     public static void main(String[] args) {
 3         HashSet set = new HashSet();//生成一个HashSet集合
 4         Iterator iterator = set.iterator();//获取迭代器的对象
 5         while (iterator.hasNext()) {//判断迭代器是否存在下一个元素
 6             //存在下一个元素
 7             //获取指定元素并将指针往下移动
 8             Object object = iterator.next();
 9             System.out.println(object);
10         }
11     }
12 }

②通过foreach遍历

1 public class Test2 {
2     public static void main(String[] args) {
3         HashSet set=new HashSet();//生成一个HashSet集合
4         for(Object object:set){
5             //遍历集合内容
6             System.out.println(object);
7         }
8     }
9 }

HashSet底层结构

Set是一个无序不重复的集合,而HashSet作为Set的子实现类,它是如何实现无序不重复的呢?

①底层使用哈希表实现无序

HashSet底层采用哈希表存储数据,哈希表是一种对于增删改查数据性能都比较好的结构,HashSet在存储数据的时候,会根据数组长度和哈希值计算出要存储的位置,从而实现无序结构。

②使用hashcode方法和equals方法实现不重复

HashSet通过hashcode方法计算哈希值,如果两者哈希值都相等,则使用equals方法来判断两者时候是同一个类型

自定义类对象添加HashSet

 1 public class Test2 {
 2     public static void main(String[] args) {
 3         HashSet<Person> set = new HashSet<>();//生成一个HashSet集合
 4         /*
 5         * TODO HashSet底层原理:
 6         * 将自定义类添加到HashSet中时,会判断hashcode方法计算的哈希值是否相同
 7         *如果哈希值不相等,则表示两者不是同一个对象,添加成功
 8         * 如果哈希值相等,则比较equals方法,如果equals方法不相等,说明不是同一个对象 ----这种情况为哈希冲突
 9         * 如果equals方法相等,则表示两者是同一个对象
10         */
11         //添加对象
12         set.add(new Person("张三",18));
13         set.add(new Person("张三",18));
14         set.add(new Person("李四",14));
15         set.add(new Person("王五",19));
16         set.add(new Person("老牛",14));
17         //遍历对象
18         for (Person person:set) {
19             System.out.println(person);
20         }
21 
22     }
23 }
24 class Person{
25     private  String name;
26     private  int age;
27 
28 
29     public Person() {
30     }
31 
32     public Person(String name, int age) {
33         this.name = name;
34         this.age = age;
35     }
36     public String getName() {
37         return name;
38     }
39     public void setName(String name) {
40         this.name = name;
41     }
42 
43     public int getAge() {
44         return age;
45     }
46     public void setAge(int age) {
47         this.age = age;
48     }
49 
50     public String toString() {
51         return "Person{name = " + name + ", age = " + age + "}";
52     }
53 }

效果展示:

 这里发现,对于自定义类中,我想表达的是属性如姓名和年龄两者相等,则是同一个对象才对呀?那为什么会出现两个{张三,18}呢?

那是因为自定义类使用的hashcode方法是Object类中的hashcode方法,而Object类中的hashcode方法是通过比较两者之间地址值,如果地址值相同表示同一个对象所以如果我们想表示属性值相同表示同一个对象,需要重写hashcode方法

重写hashcode语法

@Override
public int hashCode() {
    return Objects.hash(需要比较的属性名,.......,需要比较的属性名);
}

效果展示:

 发现为什么重写了hashCode仍然结果一样呢?

那是因为当我们只重写hashCode方法时,HashSet底层会认为{张三,18}这个对象求出的哈希值是相等的它会比较equals方法,而如果我们没有重写equals方法会直接使用Object类的equals 方法,而Object类的equals方法比较的是两者地址值,如果地址值相等,才代表是同一个对象

重写equals语法

@Override
public boolean equals(Object obj) {
   //比较两者地址值,如果相等返回true
   if(obj == this){
       return  true;
   }
    //如果两者是不同类型的返回false
   if(!(obj instanceof 自定义类)){
        return false;
   }
    //两者是相同类型
    自定义类    对象 = (自定义类) obj;
    //如果两者属性相同,则表示同一个对象
    if(比较对象的属性如果相等){
       return true;
    }
      return false;
}

效果展示:

 发现每个对象是不重复的,这样才能满足我们自己的需求。而String类它也是通过重写hashCode和equals方法判断是否为同一对象.

 TreeSet类

TreeSet底层使用的是二叉树,而TreeSet中元素要求具有排序规则,其它判断元素是否重复与HashSet一样都需要重写hashCode方法和equals方法

创建TreeSet语法:

TreeSet  名称  =new   TreeSet(); //调用无参构造器

举个例子:

public class TreeSetTest {
    public static void main(String[] args) {
        //调用无参构造器
        TreeSet set=new TreeSet();
    }
}

TreeSet存储的内容是可比较的,那如果是自定义类如何实现对象之间的可比较呢?

Java中提供两种方法来实现

比较的结果返回值:

如果返回0表示两个元素一致。
如果返回大于0,表示添加得元素比容器中得元素大。
如果返回小于0,表示添加得元素比容器中得元素小。

方法一:实现Comparable接口重写里面的方法

语法:

class 类名 implements Comparable<类名>{
    
   // 重写CompareTo方法
    @Override
    public int compareTo(类名 o) {
 
        //代码内容
}

举个例子:

 1 public class TreeSetTest {
 2     public static void main(String[] args) {
 3         //调用无参构造器
 4         TreeSet set=new TreeSet();
 5         //添加元素
 6         set.add(new People("张三",20));
 7         set.add(new People("老王",18));
 8         set.add(new People("老牛",30));
 9         set.add(new People("李四",10));
10         //遍历元素
11         for (Object o:set) {
12             People people=(People) o; 
13             System.out.println(people);
14         }
15     }
16 }
17 class People implements Comparable{
18     private String name;
19     private int age;
20 
21     @Override
22     public int compareTo(People o) {
23         24         //按照年龄进行从小到大排序(根据对象o与本类对象进行比较)
25         return this.age-o.age;
26     }
27 
28     public People() {
29     }
30 
31     public People(String name, int age) {
32         this.name = name;
33         this.age = age;
34     }
35     public String getName() {
36         return name;
37     }
38     public void setName(String name) {
39         this.name = name;
40     }
41     public int getAge() {
42         return age;
43     }
44     public void setAge(int age) {
45         this.age = age;
46     }
47     public String toString() {
48         return "People{name = " + name + ", age = " + age + "}";
49     }
50 }

 效果展示:

方法二:通过自定义排序方式,通过实现Comparator接口从而调用TreeSet的有参构造方法

语法:

class  类名  implements Compatator<类名>{

      //重写接口方法
      @Override
      public   int  compare(类名  o1,类名   o2){
            //代码内容
      }
}

举个例子:

 1 public class TreeSetTest {
 2     public static void main(String[] args) {
 3         //调用有参构造方法,传入一个实现Comparator接口的类对象
 4         TreeSet<People> set=new TreeSet<>(new People());
 5         //添加元素
 6         set.add(new People("张三",20));
 7         set.add(new People("老王",18));
 8         set.add(new People("老牛",30));
 9         set.add(new People("李四",10));
10         //遍历元素
11         for (People people:set){
12             System.out.println(people);
13         }
14     }
15 }
16 class People implements Comparator<People> {
17     private String name;
18     private int age;
19 
20     @Override
21     public int compare(People o1, People o2) {
22         //按照年龄从小到大排序
23         return o1.age-o2.age;
24     }
25 
26     public People() {
27     }
28 
29     public People(String name, int age) {
30         this.name = name;
31         this.age = age;
32     }
33     public String getName() {
34         return name;
35     }
36     public void setName(String name) {
37         this.name = name;
38     }
39     public int getAge() {
40         return age;
41     }
42     public void setAge(int age) {
43         this.age = age;
44     }
45     public String toString() {
46         return "People{name = " + name + ", age = " + age + "}";
47     }
48 }

效果展示:

发现有时候这个实现Comparator接口的类只是使用一次就会丢弃,那如果每次都直接生成一个类只使用一次就会导致资源的浪费

所以这里可以使用匿名实现类的方法来解决这个问题

语法:

Comparator<类名> comparator=new Comparator<类名>() {
            @Override
            public int compare(类名 o1, 类名 o2) {
                //代码内容
            }
};

举个例子:

 1 public class TreeSetTest {
 2     public static void main(String[] args) {
 3         //生成匿名接口类对象
 4         Comparator<String> comparator=new Comparator<String>() {
 5             @Override
 6             public int compare(String o1, String o2) {
 7                 return o1.length()-o2.length();
 8             }
 9         };
10         //调用有参构造器,传入自定义排序规则对象
11         TreeSet<String> set=new TreeSet<>(comparator);
12     }
13 }

这种方法适用于一些已经实现Comparable接口的类,当自己不想用这种排序规则时,便可以使用自定义排序.

 

posted @ 2023-11-19 12:30  和哗  阅读(17)  评论(0)    收藏  举报