java-Set集合

set 集合

集合无序且唯一

HashSet:

​ HashSet 底层数据结构是哈希表.可以保证元素的唯一性, HashSet 不是线程安全的 集合元素可以是 null.
​ 哈希表:是一个元素为链表的数组,综合了数组和链表的优点 (像新华字典一样) (JDK1.7之前) 数组+链表+二叉树( JDK1.8)
== HashSet集合底层用HashMap集合来存==


代码:

1.存储String类型

//HashSet 集合
HashSet<String> hashSet = new HashSet<>();
hashSet.add("aaa");
hashSet.add("bbb");
hashSet.add("ccc");
hashSet.add("aaa");
hashSet.add("bbb");
hashSet.add("eee");
System.out.println(hashSet);
//遍历输出
for (String s : hashSet) {
    System.out.println(s);
}

2.自定义类型:

HashSet<Student> set = new HashSet<>();
set.add(new Student("杨超越",20));
set.add(new Student("杨幂", 22));
set.add(new Student("杨紫", 20));
set.add(new Student("杨洋洋", 20));
set.add(new Student("杨超越", 20));
set.add(new Student("杨幂", 22));
set.add(new Student("杨紫", 20));
set.add(new Student("杨洋洋", 20));

for (Student student : set) {
    System.out.println(student.getName()+"==="+student.getAge());
}

Student类:

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 int hashCode() {
        //没有合理的去重写hashCode方法,就导致碰撞次数过多,以及形成的链表过长,会降低性能
        //张三 23
        //张三 23
        //李四 24
        // 王五 25   20+25*13=
        // 赵六 23    22+23*13=

        return this.name.hashCode()+age*13;//为了减少碰撞次数
    }

    @Override
    public boolean equals(Object obj) {
        System.out.println(this+"===="+obj+"调用equals方法的次数");
        if(this==obj){
            return true;
        }
        if(!(obj instanceof Student)){
            return false;
        }
        //向下转型
        Student stu= (Student) obj;
        return this.age==stu.age&&this.name.equals(stu.name);
    }
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191109130910277.bmp?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMDIyNDEx,size_16,color_FFFFFF,t_70)
     */

​ == 结论:HashSet 保证元素唯一性是靠元素重写hashCode()和equals()方法来保证的,如果不重写则无法保证。(自定义的Student类型就很好的说明了这一点。没有重写equals()方法时,比较的时地址值)==

哈希表的数据结构图:
在这里插入图片描述

LinkedHashSet

元素有序 , 并且唯一

数据结构 : 链表和哈希表
链表保证有序 哈希表保证元素唯一

// LinkedHashSet 底层数据结构是链表和哈希表,元素唯一,且有序(存取顺序一致)链表保证了有效,哈希表保证了唯一
 LinkedHashSet<String> set = new LinkedHashSet<>();
 set.add("张曼玉");
 set.add("王祖贤");
 set.add("钟楚红");
 set.add("林青霞");
 set.add("杨超越");
 set.add("张曼玉");
 set.add("王祖贤");
 set.add("钟楚红");
 set.add("林青霞");
 set.add("杨超越");
 for (String s : set) {
     System.out.println(s);
 }
使用有参构造对元素去重

代码:

ArrayList<String> strings = new ArrayList<>();
strings.add("aaa");
strings.add("bbb");
strings.add("ccc");
strings.add("ddd");
strings.add("aaa");
strings.add("bbb");
strings.add("ccc");
strings.add("ddd");
//使用有参构造,对List 集合去重
LinkedHashSet<String> strings1 = new LinkedHashSet<>(strings);
System.out.println(strings1);

TreeSet

底层数据结构是二叉树,最大特点是可以对元素排序 二叉树的数据结构 :先存入一个树根 分两个叉 存储元素时 跟树根比较 小的放在左边
大的放在右边 如果相等就不存储, 取的时候按照 左中右的顺序来取

代码:存储Integer类型

// TreeSet集合的特点: 元素唯一,并且可以对元素进行排序
//存储下列元素:  20 , 18 , 23 , 22 , 17 , 24, 19 , 18 , 24
//创建TreeSet集合
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(20);
treeSet.add(18);
treeSet.add(23);
treeSet.add(22);
treeSet.add(17);
treeSet.add(24);
treeSet.add(19);
treeSet.add(18);
treeSet.add(24);
for (Integer integer : treeSet) {
    System.out.println(integer);//顺序已经排好
}

结合代码画图:
在这里插入图片描述
TreeSet存储自定义对象并遍历

按照姓名的长度进行排序: 主要条件是姓名的长度
然后是姓名
然后是年龄

 //先比较长度
        int num=this.name.length()-o.name.length();
        //长度一样再去比较姓名内容
        int num1=num==0? this.name.compareTo(o.name):num;
        //姓名内容一样,再比较年龄
        int num2=num1==0? this.age - o.age :num1;
        return num2;

也可以是姓名长度–年龄–姓名内容

//先比较姓名长度
	int num=this.name.length()-obj.name.length();
	//如果姓名长度一样再比较年龄
	int num2=(num==0)?this.age-obj.age:num;
	//如果年龄相同 再比较姓名
	int num3=(num2==0)?this.name.compareTo(obj.name):num2;
	最后返回 num3

1.自然排序:使用无参构造
对排序元素有要求,要实现一个Comparable接口,重写compareTo()方法,通过返回的正、负、0来决定在二叉树里面放置的左右位置,0就不放

代码:

TreeSet<Student> set = new TreeSet<>();
set.add(new Student("杨超越asdfasfd", 20));
set.add(new Student("杨超越2", 20));
set.add(new Student("杨超越sdfsdf", 20));
set.add(new Student("杨子涵", 22));
set.add(new Student("杨树林sss", 25));
set.add(new Student("杨康sss", 29));
set.add(new Student("杨过ssssssssss", 23));
set.add(new Student("杨梅sssssssssssssssssssssssssssssss", 18));
set.add(new Student("杨紫dfdfdfdfdf", 16));
set.add(new Student("杨洋洋", 17));
set.add(new Student("杨洋洋asddfassd", 17));
for (Student student : set) {
    System.out.printl(student);
}
public class Student implements Comparable<Student>{//必须要实现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;

        return -num2; //根据 返回值的 正 负 0 来决定元素的左右顺序
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
//compareTo方法用的是 先比较姓名长度,长度不一样,就去比较ascall码值
public int compareTo(String anotherString) {
    int len1 = value.length;
    int len2 = anotherString.value.length;
    int lim = Math.min(len1, len2);
    char v1[] = value;
    char v2[] = anotherString.value;

    int k = 0;
    while (k < lim) {
        char c1 = v1[k];
        char c2 = v2[k];
        if (c1 != c2) {
            return c1 - c2;
        }
        k++;
    }
    return len1 - len2;
}

2.比较器排序:采用的是有参构造

这个这个构造方法传入一个比较器(Comparetor 接口)你要重写这个接口中的compare()根据此方法的返回值的正 负 0 来决定元素排列的左右顺序

```java
 TreeSet(Comparator < ? super E > comparator)
 构造一个新的空 TreeSet,它根据指定比较器进行排序。
 接口 Comparator<T> 比较器
  int compare(T o1, T o2)
   比较用来排序的两个参数。

代码:

//可以传该子类,或者用匿名内部类的方式
//MyCompareTor myCompareTor = new MyCompareTor();
//TreeSet<Student> set= new TreeSet<>(myCompareTor);
//我们采用匿名内部类来传
TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {
    @Override
    public int compare(Student s1, Student s2) {
        //根据年龄排序
        int num = s1.getAge() - s2.getAge();
        int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;

        return num2;
    }
});
set.add(new Student("杨超越asdfasfd", 20));
set.add(new Student("杨超越2", 20));
set.add(new Student("杨超越sdfsdf", 20));
set.add(new Student("杨子涵", 22));
set.add(new Student("王祖贤", 22));
set.add(new Student("王祖贤", 23));
set.add(new Student("杨树林sss", 25));
set.add(new Student("杨康sss", 29));
set.add(new Student("杨过ssssssssss", 23));
set.add(new Student("杨梅sssssssssssssssssssssssssssssss", 18));
set.add(new Student("杨紫dfdfdfdfdf", 16));
set.add(new Student("杨洋洋", 17));
set.add(new Student("杨洋洋asddfassd", 17));
for (Student student : set) {
    System.out.println(student);
}

其他一些类也可以用比较器

Integer[] arr={20,30,1,20,75,100};
//Arrays.sort(arr);
//System.out.println(Arrays.toString(arr));

System.out.println("--------------------------");
Arrays.sort(arr, new Comparator<Integer>() {
    @Override
    public int compare(Integer a, Integer b) {
        return -(a-b);
    }
});

System.out.println(Arrays.toString(arr));

ArrayList<Integer> integers = new ArrayList<>();
integers.add(100);
integers.add(100);
integers.add(2000);
integers.add(1500);
integers.add(1000);

integers.sort(new Comparator<Integer>() {
    @Override
    public int compare(Integer s1, Integer s2) {
        return s1-s2;
    }
});

System.out.println(integers);

练习:

1.需求:编写一个程序,获取10个1至20的随机数,要求随机数不能重复。

  //创建生成随机数的对象
  Random random = new Random();
 //创建TreeSet 集合
  TreeSet<Integer> treeSet = new TreeSet<>();
  while(treeSet.size()<10) {
      int i1 = random.nextInt(20) + 1;
      treeSet.add(i1);
    //  System.out.println(i1);
  }
//遍历输出
  for (Integer integer : treeSet) {
      System.out.println(integer);
  }
  System.out.println(treeSet);

  1. 需求:键盘录入3个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
 //获取总分的方法  (写在Student类中)
    public int getTotalScore(){
        return this.chineseScore+this.mathScore+this.englishScre;
    }

主方法中代码:

TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                //按照总分高低来排序学生对象
                int num = s1.getTotalScore() - s2.getTotalScore();
                //总分一样还得比较姓名
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
                return -num2;
            }
        });
        for (int i = 1; i <= 3; i++) {
            Student student = new Student();
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入第" + i + "个学生的姓名");
            String username = sc.nextLine();
            student.setName(username);
            System.out.println("请输入第" + i + "个学生的语文成绩");
            int ywScore = sc.nextInt();
            student.setChineseScore(ywScore);
            System.out.println("请输入第" + i + "个学生的数学成绩");
            int sxScore = sc.nextInt();
            student.setMathScore(sxScore);
            System.out.println("请输入第" + i + "个学生的英语成绩");
            int yyScore = sc.nextInt();
            student.setEnglishScre(yyScore);
            //把学生对象添加到集合中
            treeSet.add(student);
        }
        //将学生信息打印到控制台
        //打印表头
        System.out.println("序号" + "\t" + "姓名" + "\t" + "语文" + "\t" + "数学" + "\t" + "外语" + "\t" + "总分");
        int index = 1;
        for (Student student : treeSet) {
            System.out.println(index + "\t" + student.getName() + "\t" + student.getChineseScore() + "\t" + student.getMathScore() + "\t" + student.getEnglishScre() + "\t" + student.getTotalScore());

            index++;
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值