JavaSE:集合(三)List、Set

集合

1.List

java.util.List接口 extends Collection接口

1.List接口的特点:

  1. 有序的集合,存储元素和取出元素的顺序是一致的(存储123 取出123)
  2. 有索引,包含了一些带索引的方法
  3. 允许存储重复的元素

2.List接口中带索引的方法(特有):

public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
public E get(int index):返回集合中指定位置的元素。
public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。

注意:

  • 操作索引的时候,一定要防止索引越界异常
  • IndexOutOfBoundsException:索引越界异常,集合会报
  • ArrayIndexOutOfBoundsException:数组索引越界异常
  • StringIndexOutOfBoundsException:字符串索引越界异常
public class Demo01List {
    public static void main(String[] args) {
        //创建一个List集合对象,多态
        List<String> list = new ArrayList<>();
        //使用add方法往集合中添加元素
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("a");
        //打印集合
        System.out.println(list);//[a, b, c, d, a]  不是地址重写了toString

        //public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
        //在c和d之间添加一个itheima
        list.add(3,"it");//[a, b, c, it, d, a]
        System.out.println(list);

        //public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
        //移除元素
        String removeE = list.remove(2);
        System.out.println("被移除的元素:"+removeE);//被移除的元素:c
        System.out.println(list);//[a, b, it, d, a]

        //public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
        //把最后一个a,替换为A
        String setE = list.set(4, "A");
        System.out.println("被替换的元素:"+setE);//被替换的元素:a
        System.out.println(list);//[a, b, it, d, A]

3.List集合遍历有3种方式(接上面代码)

        //使用普通的for循环
        for(int i=0; i<list.size(); i++){
            //public E get(int index):返回集合中指定位置的元素。
            String s = list.get(i);
            System.out.println(s);
        }
        System.out.println("-----------------");
        //使用迭代器
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("-----------------");
        //使用增强for
        for (String s : list) {
            System.out.println(s);
        }

        String r = list.get(5);//IndexOutOfBoundsException: Index 5 out-of-bounds for length 5
        System.out.println(r);

    }
}

4.并发修改异常

出现的原因

  • 迭代器遍历的过程中,通过集合对象修改了集合中的元素,造成了迭代器获取元素中判断预期修改值和实际修改值不一致,则会出现:ConcurrentModificationException

通俗解释:通过迭代器来遍历的时候,在迭代器中调用next()方法的时候,会进行一个实际集合内元素大小和预期集合元素大小的比较,如果两个集合中的元素相等,才可以正常执行程序,如果你通过判断来增加集合的元素,就会造成集合内元素和预期的集合元素大小不等,就会导致并发修改异常。
解决的方案

  • 用for循环遍历,然后用集合对象做对应的操作即可

示例代码:

public class ListDemo {
    public static void main(String[] args) {
        //创建集合对象
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        //遍历集合,得到每一个元素,看有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现
        
//        Iterator<String> it = list.iterator();
//        while (it.hasNext()) {
//            String s = it.next();
//            if(s.equals("world")) {
				// 此处就是异常出现的原因
//                list.add("javaee"); 
//            }
//        }

        for(int i=0; i<list.size(); i++) {
            String s = list.get(i);
            if(s.equals("world")) {
                list.add("javaee");
            }
        }
        //输出集合对象
        System.out.println(list);
    }
}

5.ListIterator(util包 )

ListIterator:列表迭代器

  • 通过List集合的listIterator​()方法得到,所以说它是List集合特有的迭代器
  • 用于允许程序员沿任一方向遍历列表的列表的迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置(通俗解释:相对于iterator迭代器来说,iterator只能从头往后遍历,而ListIterator既可以从头往后遍历,也可以从后往前遍历,而且ListIterator迭代器可以在迭代期间增删列表中的元素。)

ListIterator中的常用方法

E next():返回迭代中的下一个元素
boolean hasNext():如果迭代具有更多元素,则返回 true
E previous​():返回列表中的上一个元素
boolean hasPrevious​():如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回 true
void add​(E e):将指定的元素插入列表

示例代码:

public class ListIteratorDemo {
    public static void main(String[] args) {
        //创建集合对象
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        //通过List集合的listIterator​()方法得到
//        ListIterator<String> lit = list.listIterator();
//        while (lit.hasNext()) {
//            String s = lit.next();
//            System.out.println(s);
//        }
//        System.out.println("--------");
//
//        while (lit.hasPrevious()) {
//            String s = lit.previous();
//            System.out.println(s);
//        }

        //获取列表迭代器
        ListIterator<String> lit = list.listIterator();
        while (lit.hasNext()) {
            String s = lit.next();
            if(s.equals("world")) {
                lit.add("javaee");
            }
        }

        System.out.println(list);

    }
}

问题:为什么ListIterator可以在迭代期间遍历?
因为ListIterator底层有一个赋值操作的过程,它将modCount赋值给了expectedModCount,就是将实际修改的值赋值给了预期的值,所以当再次判断集合内元素和预期集合内元素的时候,就不会不相等了,所以就不会出现并发修改异常。
在这里插入图片描述

6.增强for循环

增强for:

  • 简化数组和Collection集合的遍历
  • 实现Iterable接口的类允许其对象成为增强型 for语句的目标
  • 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
格式:
     for(元素数据类型 变量名 : 数组或者Collection集合) {
           //在此处使用变量即可,该变量就是元素
     }

示例代码:

public class ForDemo {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        for(int i : arr) {
            System.out.println(i);
        }
        System.out.println("--------");

        String[] strArray = {"hello","world","java"};
        for(String s : strArray) {
            System.out.println(s);
        }
        System.out.println("--------");

        List<String> list = new ArrayList<String>();
        list.add("hello");
        list.add("world");
        list.add("java");

        for(String s : list) {
            System.out.println(s);
        }
        System.out.println("--------");

        //增强for循环内部原理是一个Iterator迭代器
        /*
        for(String s : list) {
            if(s.equals("world")) {
                list.add("javaee"); //ConcurrentModificationException
            }
        }
        
        */
    }
}

增强for循环内部原理是一个Iterator迭代器

  • 解释:iterator迭代器迭代期间无法进行对集合增删元素,会造成并发修改异常,
    而增强for循环因为内部原理是一个iterator迭代器,所以也无法进行集合元素的增删。

7.List集合常用子类:ArrayList,LinkedList(util包 )

public class ArrayList
extends AbstractList
implements List, RandomAccess, Cloneable, Serializable

  1. ArrayList:底层数据结构是数组,查询快,增删慢,是不同步的(多线程),所以效率高,速度快。
  • ArrayList实际上是一个动态数组,数组的初始化长度为10,而这个10是指的逻辑上的长度,就是当一个元素添加进去的时候,剩下的9个长度不做计算。
 ArrayList集合中元素的增删是通过创建数组、复制数组元素来实现的,所以增删慢。
 ArrayList集合中查询集合元素是通过索引来实现,所以查询速度很快。
  1. LinkedList:底层数据结构是双向链表,查询慢,增删快,是不同步的(也是多线程的实现类)
    public class LinkedList
    extends AbstractSequentialList
    implements List, Deque, Cloneable, Serializable
 LinkedList因为有大量首尾元素操作的方法,所以增删快,而查询只能从头部尾部比较着查询,所以查询很慢。
注意:LinkedList不能使用多态,因为LinkedList有特有的方法,而多态不能使用自身特有的方法。

在这里插入图片描述

ArrayList集合练习:
需求:创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合

思路:

  • 1:定义学生类
    2:创建ArrayList集合对象
    3:创建学生对象
    4:把学生添加到集合
    5:遍历集合

  • 迭代器:集合特有的遍历方式
    普通for:带有索引的遍历方式
    增强for:最方便的遍历方式

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;
    }
}

ArrayListDemo测试类

public class ArrayListDemo {
    public static void main(String[] args) {
        //创建ArrayList集合对象
        ArrayList<Student> array = new ArrayList<Student>();

        //创建学生对象
        Student s1 = new Student("林青霞", 30);
        Student s2 = new Student("张曼玉", 35);
        Student s3 = new Student("王祖贤", 33);

        //把学生添加到集合
        array.add(s1);
        array.add(s2);
        array.add(s3);

        //迭代器:集合特有的遍历方式
        Iterator<Student> it = array.iterator();
        while (it.hasNext()) {
            Student s = it.next();
            System.out.println(s.getName() + "," + s.getAge());
        }
        System.out.println("--------");

        //普通for:带有索引的遍历方式
        for(int i=0; i<array.size(); i++) {
            Student s = array.get(i);
            System.out.println(s.getName() + "," + s.getAge());
        }
        System.out.println("--------");

        //增强for:最方便的遍历方式
        for(Student s : array) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

LinkedList集合练习

LinkedList集合的特有功能:

public void addFirst(E e):在该列表开头插入指定的元素
public void addLast(E e):将指定的元素追加到此列表的末尾
public E getFirst():返回此列表中的第一个元素
public E getLast():返回此列表中的最后一个元素
public E removeFirst():从此列表中删除并返回第一个元素
public E removeLast():从此列表中删除并返回最后一个元素

示例代码:

public class LinkedListDemo {
    public static void main(String[] args) {
        //创建集合对象
        LinkedList<String> linkedList = new LinkedList<String>();

        linkedList.add("hello");
        linkedList.add("world");
        linkedList.add("java");

        public void addFirst(E e):在该列表开头插入指定的元素
        public void addLast(E e):将指定的元素追加到此列表的末尾
        linkedList.addFirst("javase");
        linkedList.addLast("javaee");

        public E getFirst():返回此列表中的第一个元素
        public E getLast():返回此列表中的最后一个元素
        System.out.println(linkedList.getFirst());
        System.out.println(linkedList.getLast());

        public E removeFirst():从此列表中删除并返回第一个元素
        public E removeLast():从此列表中删除并返回最后一个元素
        System.out.println(linkedList.removeFirst());
        System.out.println(linkedList.removeLast());

        System.out.println(linkedList);
    }
}

8.Vector集合(了解,已过时)

Vector集合可以实现可增长的对象数组,Vector集合和ArrayList类似,底层都是数组
但是Vector集合是同步的(单线程),速度慢,所以1.2版本后,被ArrayList替代了。

2.Set

1. Set集合特点

    不包含重复元素的集合
    没有带索引的方法,所以不能使用普通for循环遍历

HashSet:对集合的迭代顺序不作任何保证

示例代码:

public class SetDemo {
    public static void main(String[] args) {
        //创建集合对象
        Set<String> set = new HashSet<String>();

        //添加元素
        set.add("hello");
        set.add("world");
        set.add("java");
        //不包含重复元素的集合
        set.add("world");

        //遍历
        for(String s : set) {
            System.out.println(s);
        }

    }
}

2.哈希值

哈希值:
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

通俗解释:哈希值是一个十进制的的整数,由系统随机给出(是一个对象的地址,一个逻辑地址,一个模拟出来的地址,不是数据实际存储的物理地址)
Object类中有一个方法可以获取对象的哈希值

public int hashCode():返回对象的哈希码值

对象的哈希值的特点:

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
  • 默认情况下,不同对象的哈希值是不相同的通过方法重写,可以实现不同对象的哈希值是相同的。

示例代码:(两个类,一个Student类,一个测试类)

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() {
        return 0;
    }
}

测试类
public class HashDemo {
    public static void main(String[] args) {
        //创建学生对象
        Student s1 = new Student("林冲",25);

        //同一个对象多次调用hashCode()方法返回的哈希值是相同的
        System.out.println(s1.hashCode()); //1060830840
        System.out.println(s1.hashCode()); //1060830840
        System.out.println("--------");

        Student s2 = new Student("林青霞",30);

        //默认情况下,不同对象的哈希值是不相同的
        //通过方法重写,可以实现不同对象的哈希值是相同的
        System.out.println(s2.hashCode()); //2137211482
        System.out.println("--------");

        System.out.println("hello".hashCode()); //99162322
        System.out.println("world".hashCode()); //113318802
        System.out.println("java".hashCode()); //3254818

        System.out.println("world".hashCode()); //113318802
        System.out.println("--------");

        System.out.println("重地".hashCode()); //1179395
        System.out.println("通话".hashCode()); //1179395


    }
}

3.HashSet集合( util包 )

HashSet集合特点
实现不同步。(多线程),线程不安全。

  1. 底层数据结构是哈希表
  2. 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
  3. 没有带索引的方法,所以不能使用普通for循环遍历
  4. 由于是Set集合,所以是不包含重复元素的集合

示例代码:

public class HashSetDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        HashSet<String> hs = new HashSet<String>();

        //添加元素
        hs.add("hello");
        hs.add("world");
        hs.add("java");

        hs.add("world");

        //遍历
        for(String s : hs) {
            System.out.println(s);
        }
    }
}

4.HashSet集合保证元素唯一性的源码分析

源码解析:
1.创建了一个HashSet对象

2.通过对象调用add方法往集合中添加了三个元素

3.首先调用add()方法传参,参数列表中的e就是字符串参数,add()方法中又调用了一个put()方法。

4.put()方法中参数的key就是字符串参数,put()方法中又调用了一个putVal()方法,参数列表中,调用了一个hash()方法,hash()方法中传入了key,也就是字符串参数。

5.hash()方法中传入参数key,然后将key也就是字符串参数与null作比较,判断是不是为空,如果为空,返回0,另外通过字符串参数调用hashCode()方法获得字符串的哈希值并赋值给变量h,并返回。

6.所以在put()方法中的putVal()方法中,参数列表的一个值是字符串的哈希值,第二个参数为add方法中传入的字符串。

7.接着看putVal()方法,putVal()方法中的参数列表,第一个值就是传入的哈希值,第二个就是传入的字符串元素。Node<K,V>[] tab; 看到这个内容,其实哈希表就是一个数组结构,Node是一个节点,所以这是一个元素类节点的数组。

8.接着看putVal()方法里的语句,
第一个if判断,判断的是tab这个哈希表是否为null,或者长度是否为0,如果为0,说明哈希表没
有进行初始化,所以要进行初始化。

第二个if判断,是根据对象的哈希值,计算对象的存储位置,并且还判断是否等于null,如果等于null,代表该位置没有元素,就存储元素。如果该位置有元素,就用现在要存入的元素和以前存入的元素比较哈希值,
如果哈希值不同,会继续向下执行,把元素添加到集合中
如果哈希值相同,会调用对象的equals()方法比较。 (对应代码中的 key.equals(k))
如果返回false,会继续向下执行,把元素添加到集合
如果返回true,说明元素重复,不存储。

//创建集合对象
HashSet<String> hs = new HashSet<String>();

//添加元素
hs.add("hello");
hs.add("world");
hs.add("java");
----------------------------------------------

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

//hash值和元素的hashCode()方法相关
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;

    //根据对象的哈希值计算对象的存储位置,如果该位置没有元素,就存储元素
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        /*
            存入的元素和以前的元素比较哈希值
                如果哈希值不同,会继续向下执行,把元素添加到集合
                如果哈希值相同,会调用对象的equals()方法比较
                    如果返回false,会继续向下执行,把元素添加到集合
                    如果返回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;
}

图解:
在这里插入图片描述

哈希表

哈希表是一个数组+链表/红黑树的结合体,默认初始容量是16,索引就是0-15.

将字符串元素添加到哈希表中

  1. 首先计算字符串元素的哈希值。
  2. 通过哈希值对16取余(%),通过得出的数字,将元素存储到数字所对应的索引上。
  3. 如果三个元素的哈希值%16后得出的数字相同,哪个元素方法先执行,就先把哪个元素添加进去,然后第二个元素发现索引上有元素了,那么这个元素就要与已经存储的元素进行比较。
  4. 首先比较的是哈希值,如果哈希值不相同,就将元素存储到相同索引下,接下来第三个元素发现索引下依然还是有元素,那么第三个元素就要与第一个元素进行比较,然后在同第二个元素进行比较。
  5. (1)还是相同步骤,先比较哈希值,如果哈希值不相同,就将元素存储到相同索引的下方,
    (2)如果哈希值相同,就继续判断元素的内容是否相同,如果还是相同,就不会将该元素存储到索引下。
    (3)如果哈希值相同,而元素的内容不同,就将该元素存储到索引的下方。

总结:
1.equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比是绝对可靠。

2.hashCode()相等的两个对象他们的equal()不一定相等,就是hashCode()不是绝对可靠。

hashCode是对象在内存地址通过hash算法得到的哈希码;

比较两个对象是否相等:

1.首先比较hashcode ,如果hashcode相等则进一步比较equals,不相等则两个对象肯定不相等;

图解:
在这里插入图片描述

5.HashSet集合案例

需求:
创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
要求:学生对象的成员变量值相同,我们就认为是同一个对象

思路:

  • 1:定义学生类
    2:创建HashSet集合对象
    3:创建学生对象
    4:把学生添加到集合
    5:遍历集合(增强for)
public class HashSetDemo02 {
    public static void main(String[] args) {
        //创建HashSet集合对象
        HashSet<Student> hs = new HashSet<Student>();

        //创建学生对象
        Student s1 = new Student("张飞", 30);
        Student s2 = new Student("关羽", 35);
        Student s3 = new Student("刘备", 33);

        Student s4 = new Student("赵子龙", 33);

        //把学生添加到集合
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);

        //遍历集合(增强for)
        for (Student s : hs) {
            System.out.println(s.getName() + "," + s.getAge());
        }

    }
}

6.LinkedHashSet集合 (util包)

不同步(多线程),线程不安全
public class LinkedHashSet
extends HashSet
implements Set, Cloneable, Serializable

LinkedHashSet集合特点

  1. 哈希表和链表实现的Set接口,具有可预测的迭代次序
  2. 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
  3. 由哈希表保证元素唯一,也就是说没有重复的元素

LinkedHashSet集合底层是由哈希表和双向链表组成, 具有可预测的迭代次序,
即存储的元素和取出的元素是一致的。(存取元素一致是由链表来保证的。)
LinkedHashSet集合元素不能重复是由哈希表来保证的。

7.TreeSet集合概述和特点(util包)

TreeSet底层是由红黑树实现的
TreeSet集合不同步(多线程),线程不安全
public class TreeSet
extends AbstractSet
implements NavigableSet, Cloneable, Serializable

TreeSet集合特点

  1. 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法:

    TreeSet():根据其元素的自然排序进行排序(根据整数的123,字母的abc等排序)
    TreeSet(Comparator comparator) :根据指定的比较器进行排序

  2. 没有带索引的方法,所以不能使用普通for循环遍历

  3. 由于是Set集合,所以不包含重复元素的集合

  4. 可以通过字符串直接调用compareTo方法,因为String类实现了Compareable接口在这里插入图片描述

代码举例:
存储整数,遍历(自然排序)

public class TreeSetDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Integer> ts = new TreeSet<Integer>();

        //添加元素
        ts.add(10);
        ts.add(40);
        ts.add(30);
        ts.add(50);
        ts.add(20);

        ts.add(30);

        //遍历集合
        for(Integer i : ts) {
            System.out.println(i);
        }
    }
}

运行结果:
10
20
30
40
50

8.自然排序Comparable的使用

存储学生对象并遍历,创建集合使用无参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序

要对一个类进行自定义的自然排序

  • 实现Comparable<>接口,并重写compareTo方法
  • compareTo方法中,
    如果return 0,默认认为是重复元素,不添加元素。
    如果return 1,默认认为不是重复元素,并按照升序存储元素。
    如果return -1,默认认为不是重复元素,并按照降序存储元素。
    如果想要升序排列,this.属性 - 传入的对象.属性值 (this在前)
    如果想要降序排列,传入的对象.属性 - this.属性值 (this在后)
  • 多条件排序的时候:
    适用场景:比如当age相同,名字不同的时候
    int num = 传入的对象.age - this.age ; (当num为0时,表示是重复元素。)
    这就需要使用 int num2 num== 0?this.name.compareTo(传入的对象.name):num
    因为String类实现了Comparable< String > 接口,所以可以直接调用compareTo方法比较。

代码实现:

Student类
public class Student implements Comparable<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 compareTo(Student s) {
//        return 0;
//        return 1;
//        return -1;
        //按照年龄从小到大排序
       int num = this.age - s.age;
//        int num = s.age - this.age;
        //年龄相同时,按照姓名的字母顺序排序
       int num2 = num==0?this.name.compareTo(s.name):num;
        return num2;
    }
}
测试类
public class TreeSetDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>();

        //创建学生对象
        Student s1 = new Student("xishi", 29);
        Student s2 = new Student("wangzhaojun", 28);
        Student s3 = new Student("diaochan", 30);
        Student s4 = new Student("yangyuhuan", 33);

        Student s5 = new Student("linqingxia",33);
        Student s6 = new Student("linqingxia",33);

        //把学生添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历集合
        for (Student s : ts) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

9.比较器排序

结论

  • 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
  • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare​(T o1,T o2)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
/*
    存储学生对象并遍历,创建TreeSet集合使用带参构造方法
    要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
 */
public class TreeSetDemo {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                //this.age - s.age
                //s1,s2
                int num = s1.getAge() - s2.getAge();
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
                return num2;
            }
        });

        //创建学生对象
        Student s1 = new Student("xishi", 29);
        Student s2 = new Student("wangzhaojun", 28);
        Student s3 = new Student("diaochan", 30);
        Student s4 = new Student("yangyuhuan", 33);

        Student s5 = new Student("linqingxia",33);
        Student s6 = new Student("linqingxia",33);

        //把学生添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历集合
        for (Student s : ts) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

3.成绩排序

需求:用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合
要求:按照总分从高到低出现
思路:

  1. 定义学生类
  2. 创建TreeSet集合对象,通过比较器排序进行排序
  3. 创建学生对象
  4. 把学生对象添加到集合
  5. 遍历集合

示例代码:

Student类
public class Student  {
    private String name;
    private int Chinese;
    private int math;

    public Student() {
    }

    public Student(String name, int Chinese, int math) {
        this.name = name;
        this.Chinese = Chinese;
        this.math = math;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getChinese() {
        return Chinese;
    }

    public void setYuwen(int yuwen) {
        this.Chinese = yuwen;
    }

    public int getMath() {
        return math;
    }

    public void setMath(int math) {
        this.math = math;
    }

    public int getSum() {
        return this.Chinese+this.math;
    }
}

测试类
public class Test {
    public static void main(String[] args) {
        TreeSet<Student> list = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
               //int num = (s2.getMath()+s2.getChinese()) - (s1.getMath()+s1.getChinese());
               int num = s2.getSum() - s1.getSum();
               int num2 = num == 0 ? s1.getChinese() - s2.getChinese():num;
               int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()):num2;
               return num3;
            }
        });

        Student s1 = new Student("林冲",60,70);
        Student s2 = new Student("张飞",80,60);
        Student s3 = new Student("赵云",90,90);
        Student s4 = new Student("刘备",89,91);
        Student s5 = new Student("关羽",89,91);

        list.add(s1);
        list.add(s2);
        list.add(s3);
        list.add(s4);
        list.add(s5);

        for (Student student : list) {
            System.out.println("姓名:"+student.getName() + "语文:" + student.getChinese()+"数学:"+student.getMath());
        }
    }
}

4.不重复的随机数

需求:编写一个程序,获取10个1-20之间的随机数,要求随机数不能重复,并在控制台输出

示例代码:

public class Test2 {
    public static void main(String[] args) {
        // 创建Set集合对象
        //Set<Integer> set = new HashSet<>();

        //使用TreeSet集合
        Set<Integer> set = new TreeSet<Integer>();

        // 创建随机数对象
        Random r = new Random();

        // 判断集合的长度是否小于10
        while (set.size()<10){
            int i = r.nextInt(20) + 1;
            set.add(i);
        }

        //遍历集合
        for (Integer count : set) {
            System.out.println(count);
        }

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值