天天看点

Javase-day14-数据结构(与集合相关),List集合,Set集合一、与集合相关的数据结构二、List集合三、List接口的子类(实现类)四、Set接口五、Set接口的子类(实现类)练习

文章目录

  • 一、与集合相关的数据结构
  • 二、List集合
    • 2.1 List集合概述
    • 2.2 List接口中带索引特有的方法
    • 2.3 遍历List集合的三种方法
  • 三、List接口的子类(实现类)
    • 3.1 ArrayList集合(ArrayList类型的对象)
    • 3.2 LinkedList集合(LinkedList类型的对象)
    • 3.3 Vector集合(Vector类型的对象)(了解)
  • 四、Set接口
    • Set接口概述
  • 五、Set接口的子类(实现类)
    • 5.1 HashSet集合
      • 5.1.1 HashSet存储数据的结构——哈希表
      • 5.1.2 HashSet集合概述和特点
      • 5.1.3 HashSet保证元素唯一性源码分析
      • 5.1.4 常见数据结构之【哈希表】
    • 5.2 LinkedHashSet集合概述和特点(继承自HashSet)
    • 5.3 TreeSet集合概述和特点
      • 5.3.1 自然排序Comparable接口的使用
      • 5.3.2 比较器排序Comparator接口的使用
  • 练习

一、与集合相关的数据结构

回到目录

  • 栈:是一种先进后出的模型。
  • 队列:是一种先进先出的模型。
  • 数组:查询快,添删慢的模型。
  • 链表:对于数组来说,是一种增删快,查询慢的模型。
  • 红黑树

集合的继承关系

Javase-day14-数据结构(与集合相关),List集合,Set集合一、与集合相关的数据结构二、List集合三、List接口的子类(实现类)四、Set接口五、Set接口的子类(实现类)练习

二、List集合

回到目录

2.1 List集合概述

java.util.List

接口继承自

Collection

接口,是单列集合的一个重要分支,一般习惯将实现了List接口的对象称为List集合。

List接口的三大特点:

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

2.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:字符串索引越界异常

2.3 遍历List集合的三种方法

  • 普通for循环(因为List集合有索引,可以用for循环遍历);
  • 迭代器遍历List集合;
  • 增强for循环
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/*
    java.util.List接口 extends Collection接口
    List接口的特点:
        1.有序的集合,存储元素和取出元素的顺序是一致的(存储123 取出123)
        2.有索引,包含了一些带索引的方法
        3.允许存储重复的元素

    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<>();		// List集合的泛型是字符串
        //使用add方法往集合中添加字符串元素
        list.add("古力娜扎");
        list.add("迪丽热巴");
        list.add("古力娜扎");
        list.add("玛尔扎哈");
        list.add("玛尔扎哈");
        System.out.println(list);   // [古力娜扎, 迪丽热巴, 古力娜扎, 玛尔扎哈, 玛尔扎哈]
        // 而且打印的不是地址值,说明重写了toString方法

        // public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
        // 在 古力娜扎和玛尔扎哈 之间添加一个cxk
        list.add(3, "cxk");
        System.out.println(list);   // [古力娜扎, 迪丽热巴, 古力娜扎, cxk, 玛尔扎哈, 玛尔扎哈]

        // public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
        // 移除元素cxk,cxk的索引是3
        String removeE = list.remove(3);    // removeE 得到的是被移除的元素
        System.out.println("被移除的元素:" + removeE);    // 被移除的元素:cxk
        System.out.println(list);   // [古力娜扎, 迪丽热巴, 古力娜扎, 玛尔扎哈, 玛尔扎哈]

        // public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
        // 把第一个“古力娜扎”, 替换为“萨扬娜拉”
        String setE = list.set(0, "萨扬娜拉");       // setE 得到的是被替换前的元素
        System.out.println("被替换的元素:" + setE);   // 被替换的元素:古力娜扎
        System.out.println(list);   // [萨扬娜拉, 迪丽热巴, 古力娜扎, 玛尔扎哈, 玛尔扎哈]

        System.out.println("==========================");

        // 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();		// s是下一个元素
            System.out.println(s);
        }
        System.out.println("==========================");

        // 使用增强for
        for (String s : list) {
            System.out.println(s);
        }
		// 索引越界异常
        String r = list.get(5);     
        System.out.println(r);	// java.lang.IndexOutOfBoundsException: Index 5 out of bounds for length 5
    }
}
           

三、List接口的子类(实现类)

回到目录

3.1 ArrayList集合(ArrayList类型的对象)

  

java.util.ArrayList

集合数据存储的结构是【数组结构】,增删比较慢,查询快。通常我们使用最多的功能就是查询数据、遍历数据,所以

ArrayList

是最常用的集合。

  

ArrayList

集合不是同步的(不是同步就是多线程),效率比较高。

import java.util.ArrayList;
import java.util.Iterator;
/*
ArrayList集合存储学生对象, 用三种方式遍历, (Student类里面私有成员变量为name, age)。
*/
public class ArrayListDemo{
    public static void main(String[] args) {
        // 创建ArrayList集合
        ArrayList<Student> array = new ArrayList<>();
        // 创建学生对象
        Student s1 = new Student("迪丽热巴",25);
        Student s2 = new Student("古力娜扎",26);
        Student s3 = new Student("玛尔扎哈",40);
        // 把学生添加到集合
        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());
        }
    }
}
           

3.2 LinkedList集合(LinkedList类型的对象)

  

java.util.LinkedList

集合数据存储的结构是【链表结构】,增删快,查询慢。也是多线程的。

LinkedList实现类中提供了大量特有的【首尾操作】方法:

  • public void addFirst(E e):将指定元素插入此列表的开头。
  • public void addLast(E e):将指定元素添加到此列表的结尾。
  • public void push(E e):将元素推入此列表所表示的堆栈。 【这个push方法等效于addFist方法】
  • public E getFirst():返回此列表的第一个元素。
  • public E getLast():返回此列表的最后一个元素。
  • public E removeFirst():移除并返回此列表的第一个元素。
  • public E removeLast():移除并返回此列表的最后一个元素。
  • public E pop():从此列表所表示的堆栈处弹出一个元素。 【这个pop方法等效于removeFirst方法】
  • public boolean isEmpty():如果列表不包含元素,则返回true。

使用LinkedList集合要注意:不能使用多态(多态,完成了向上转型(转为List接口了),不能使用实现类(LinkedList)特有的方法)

LinkedList集合是一个实现类,直接new一个对象就可以了。

创建LinkedList集合:

LinkedList<String> linked = new LinkedList<>();

以删除集合元素的方法为例:(增删改查都差不多)

import java.util.LinkedList;
public class Demo02LinkedList {
    public static void main(String[] args) {
        show1();
    }
    private static void show1() {
        // 创建LinkedList集合对象,LinkedList集合是一个实现类,直接new对象
        LinkedList<String> linked = new LinkedList<>();

        // 使用add方法往集合中添加元素
        linked.add("迪丽热巴");
        linked.add("古力娜扎");
        linked.add("玛尔扎哈");
        System.out.println(linked);     // [迪丽热巴, 古力娜扎, 玛尔扎哈]

        // pop方法等效于removeFirst()方法
        String first = linked.pop();
        System.out.println("被移除的第一个元素:" + first);   // 被移除的第一个元素:迪丽热巴

        // public E removeLast():移除并返回此列表的最后一个元素。
        String last = linked.removeLast();
        System.out.println("被移除的最后一个元素:" + last);   // 被移除的最后一个元素:玛尔扎哈
        System.out.println(linked);     // [古力娜扎]
    }
}
           

3.3 Vector集合(Vector类型的对象)(了解)

  Vector 类可以实现可增长的【对象数组】,与新 collection 实现不同,Vector 是同步的(单线程,速度慢)。在JDK1.2+,被ArrayList集合取代了。

  • Vector 的 iterator 和 listIterator 方法所返回的迭代器是快速失败的;
  • Vector 的 elements 方法返回的 Enumeration(枚举) 不是 快速失败的。

四、Set接口

回到目录

java.util.Set

接口和

java.util.List

接口一样,都是继承于

Collection

接口。所以,Set接口的方法继承自Collection接口,所以常用的方法是一致的,之前在Collection接口中讲了常用方法,这里就不再重复了。

Set接口概述

Set接口的特点:

  • 不允许存储重复的元素;
  • 没有索引,没有带索引的方法,也不能使用普通的for循环遍历

五、Set接口的子类(实现类)

接口不能直接创建对象,要用到Set接口的实现类,Set接口有两个实现类:

java.util.HashSet

集合和

java.util.LinkedHashSet

集合。

5.1 HashSet集合

回到目录

HashSet集合的特点:

  • 【不允许存储重复】的元素;
  • 【无索引】,没有带索引的方法,也不能使用普通的for循环遍历;
  • 是一个【无序】的集合,存储元素和取出元素的顺序有可能不一致,对集合的迭代顺序不作任何保证;
  • 底层是一个【哈希表结构】(查询的速度非常的快)。

HashSet集合的遍历方法有两个:(没有索引,不能用普通for)

  • Iterator迭代器;
  • 增强for
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Demo01Set {
    public static void main(String[] args) {
    	// 多态的写法可以,不用多态的写法也可以,直接用通过HashSet实现类创建对象—— HashSet<String> set = new HashSet<>();
        Set<String> set = new HashSet<>();    
        // 使用add方法往集合中添加元素
        set.add("迪丽热巴");
        set.add("古力娜扎");
        set.add("玛尔扎哈");
        set.add("迪丽热巴");
   		//使用迭代器遍历集合,Collection接口所有的实现类里都有iterator方法
        Iterator<String> it = set.iterator();	// 调用iterator()方法,返回一个Iterator接口的实现类对象。
        while (it.hasNext()){
            String s = it.next();	// 快捷键【Ctrl+Alt+V】
            System.out.println(s);  // 玛尔扎哈 迪丽热巴 古力娜扎 ———— 【没有顺序,没有重复的元素】
        }
        System.out.println("====================");
        //使用增强for遍历集合
        for (String a : set) {
            System.out.println(a);
        }
    }
}
           

5.1.1 HashSet存储数据的结构——哈希表

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

在Object类中的

hashCode()

方法,可以获取对象的哈希值。**
  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的;
  • 默认情况下(没重写hashCode方法),不同对象的哈希值是不同的;
  • 通过重写hashCode方法,可以实现不同对象的哈希值相同。(String类中重写了Obejct类中的hashCode方法)
int hashCode() :返回该对象的哈希码值。
hashCode方法的源码:
    public native int hashCode();	// native:代表该方法调用的是本地操作系统的方法
           
定义一个Person类(默认继承于Object类):
public class Person /*extends Object*/{
}

使用Person类:
public class Demo01HashCode {
    public static void main(String[] args) {
        // Person类继承了Object类,所以可以使用Object类的hashCode方法
        Person p1 = new Person();
        int h1 = p1.hashCode();
        System.out.println(h1);     // 1967205423
		
        Person p2 = new Person();
        int h2 = p2.hashCode();
        System.out.println(h2);     // 42121758
		// 不同对象哈希值不同
		
        /*
            toString方法的源码:
            return getClass().getName() + "@" + Integer.toHexString(hashCode());
        */
        System.out.println(p1);         // 包名@75412c2f
        System.out.println(p2);         // 包名@282ba1e
        System.out.println(p1 == p2);   // false

        /*
            String类的哈希值
            String类中重写了Obejct类中的hashCode方法
        */
        // 字符串常量池,用的是一个对象,地址值也相同
        System.out.println("Hello".hashCode());     // 69609650
        System.out.println("Java".hashCode());      // 2301506

        String s1 = new String("abc");
        System.out.println(s1.hashCode());        // 96354
        System.out.println("abc".hashCode());     // 96354

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

5.1.2 HashSet集合概述和特点

HashSet集合特点:

  • 底层数据结构是【哈希表】——( 哈希表是 数组+链表 )
  • 对集合的迭代顺序不做保证,也就是说【不保证】存储和取出的元素顺序一致
  • 没有带索引的方法,所以不能使用【普通for】遍历
  • 由于是Set集合,所以是不包含重复元素的集合

HashSet集合练习:

import java.util.HashSet;

public class HashSetDemo01 {
    public static void main(String[] args) {
        // 创建HashSet对象
        HashSet<String> hs = new HashSet<>();
        // 添加对象
        hs.add("迪丽热巴");
        hs.add("古力娜扎");
        hs.add("玛尔扎哈");
        hs.add("玛尔扎哈");
        // 增强for遍历
        for (String s : hs) {
            System.out.println(s);
        }
    }
}
           

5.1.3 HashSet保证元素唯一性源码分析

HashSet集合添加一个元素的过程:

Javase-day14-数据结构(与集合相关),List集合,Set集合一、与集合相关的数据结构二、List集合三、List接口的子类(实现类)四、Set接口五、Set接口的子类(实现类)练习

HashSet集合存储元素:

  • 要保证元素的唯一性,需要重写hashCode()和equals()方法(equals方法比较内容是否相同)

    源码 :

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

// 添加对象
hs.add("迪丽热巴");
hs.add("古力娜扎");
hs.add("玛尔扎哈");
hs.add("玛尔扎哈");
-----------------------------------------------
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;
    }
           

5.1.4 常见数据结构之【哈希表】

哈希表 : (哈希表中没有重复的元素)

  • JDK8之前, 底层采用【数组+链表】实现, 可以说是一个元素为链表的数组
  • JDK8之后, 在长度比较长时, 底层实现了优化

  对象的哈希值对16取余就是在哈希表(数组)中的索引位置。

  当两对象间的哈希值相同时,比内容。如果内容也相同(重复元素),就不存储;如果内容不相同,就在数组中用链表连着。

Javase-day14-数据结构(与集合相关),List集合,Set集合一、与集合相关的数据结构二、List集合三、List接口的子类(实现类)四、Set接口五、Set接口的子类(实现类)练习

练习:HashSet集合存储学生对象并遍历(要求:如果两对象的成员变量值相同,就认为是一个对象)

  • 已经定义好了Student类,有私有成员变量name和age。
  • 要重写Student类中的hashCode()和equals()方法!要不然,还是可以把相同的成员变量添加进去,快捷键自动生成。
import java.util.HashSet;
import java.util.Iterator;

public class HashSetDemo02 {
    public static void main(String[] args) {
        // 创建HashSet集合对象
        HashSet<Student> hs = new HashSet<>();
        // 创建学生对象
        Student s1 = new Student("迪丽热巴",20);
        Student s2 = new Student("古力娜扎",21);
        Student s3 = new Student("玛尔扎哈",30);
        // 注意: s4引用的Student对象跟s1对象的成员变量的值是一样的,要视为一个对象, 不能把s4添加进去
        Student s4 = new Student("迪丽热巴",20);

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

        // 用迭代器遍历集合
        Iterator<Student> it = hs.iterator();
        while(it.hasNext()){
            Student a = it.next();
            System.out.println(a.getName() + "," + a.getAge());
        }
    }
}
           

没有重写Student类中的equals()和hashCode()方法时,把重复的添加到HashSet集合中了:

Javase-day14-数据结构(与集合相关),List集合,Set集合一、与集合相关的数据结构二、List集合三、List接口的子类(实现类)四、Set接口五、Set接口的子类(实现类)练习

自动生成,重写Student类的equals和hashCode方法后,重复的将被视为一个对象

Student类重写:

Javase-day14-数据结构(与集合相关),List集合,Set集合一、与集合相关的数据结构二、List集合三、List接口的子类(实现类)四、Set接口五、Set接口的子类(实现类)练习

控制台输出:

Javase-day14-数据结构(与集合相关),List集合,Set集合一、与集合相关的数据结构二、List集合三、List接口的子类(实现类)四、Set接口五、Set接口的子类(实现类)练习

5.2 LinkedHashSet集合概述和特点(继承自HashSet)

LinkedHashSet集合特点:

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

练习:用LInkedHashSet存储字符串并遍历

import java.util.Iterator;
import java.util.LinkedHashSet;
public class LinkedHashSetDemo {
    public static void main(String[] args) {
        // 创建LInkedHashSet集合对象
        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();

        // 添加元素
        linkedHashSet.add("迪丽热巴");
        linkedHashSet.add("古力娜扎");
        linkedHashSet.add("玛尔扎哈");
        linkedHashSet.add("迪丽热巴");
        // 迭代器遍历
        Iterator<String> it = linkedHashSet.iterator();
        while(it.hasNext()){
            String a = it.next();
            System.out.println(a);
        }
        System.out.println("===================");
        // 增强for遍历
        for (String s : linkedHashSet) {
            System.out.println(s);
        }
    }
}
           

控制台输出:(输出和输入的顺序相同,哈希表保证了元素的唯一性)

Javase-day14-数据结构(与集合相关),List集合,Set集合一、与集合相关的数据结构二、List集合三、List接口的子类(实现类)四、Set接口五、Set接口的子类(实现类)练习

5.3 TreeSet集合概述和特点

回到目录

TreeSet集合的特点:

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

    无参构造TreeSet():根据其元素的【自然排序】(从小到大)进行排序

    带参构造TreeSet(Comparator comparator):根据指定的比较器进行排序

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

TreeSet集合练习:存储整数并遍历

import java.util.TreeSet;
public class TreeSetDemo01 {
    public static void main(String[] args) {
        TreeSet<Integer> ts = new TreeSet<>();	// 由于调用的是无参构造,按照自然排序,从小到大排
        // 添加元素
        ts.add(10);
        ts.add(50);
        ts.add(40);
        ts.add(20);
 		// 无重复元素
 		ts.add(20);
        // TreeSet没得索引, 增强for遍历
        for(Integer i : ts){
            System.out.println(i);
        }
    }
}
           

5.3.1 自然排序Comparable接口的使用

需求:

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

结论:

  • 用TreeSet集合存储自定义对象,无参构造方法使用的是【自然排序】对元素进行排序的
  • 自然排序,就是让元素所属的类(例子是Student类)实现Comparable接口,重写comparaTo(T o)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写,比如说年龄相同时,按照姓名排序。
Student类:
import java.util.Objects;
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;
    }
    
    // 重写compareTo方法
    @Override
    public int compareTo(Student o) {
//        return 0;  返回值如果是0,被认为是重复元素,就不会添加后面的元素了,只会有一个值
//        return 1;  按照存储的顺序输出的
//        return -1; 按照存储倒着输出

        // 我们的需求是:按照 年龄从小到大排序(主要条件)
        int num = this.age - o.age;     // 按照年龄的升序【升序this放在前面】
        // 换成 int num = o.age - this.age;就是按照年龄的降序来排列

        // 年龄相同时,按照姓名的字母顺序排序
        int num2 = num == 0 ? this.name.compareTo(o.name) : num;
        return num2;
    }
}
           
实现需求:
import java.util.TreeSet;
public class TreeSetDemo02 {
    public static void main(String[] args) {
        // 创建 TreeSet 集合
        TreeSet<Student> ts = new TreeSet<>();
        // 创建学生对象
        Student s1 = new Student("ddd",23);
        Student s2 = new Student("aaa",30);
        Student s3 = new Student("ccc",21);
        Student s4 = new Student("ddd",22);
        Student s5 = new Student("kkk",22);

        Student s6 = new Student("cxk",22);
        Student s7 = new Student("cxk",22);		// 因为重写了hashCode和equals方法,只能存一个cxk
        // 把学生添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        ts.add(s7);
        // 增强for遍历
        for(Student s : ts){
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}
           

5.3.2 比较器排序Comparator接口的使用

需求:

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

结论:

  • 用TreeSet集合存储自定义对象,【带参构造方法】使用的是【比较器排序】的
  • 比较器排序,就是,让集合构造方法接受Comparator的实现类对象,重写compare(T o1, T o2)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主、次要条件来写
学生类:
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;
    }
}
           
import java.util.Comparator;
import java.util.TreeSet;
// 创建TreeSet集合使用【带参构造方法】

public class TreeSetDemo {
    public static void main(String[] args) {
        // 创建 TreeSet 集合,要使用【带参构造方法】,用【匿名内部类】实现Comparator接口
        TreeSet<Student> ts = new TreeSet<>(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;		// 次要条件——姓名
                // 比较s1和s2的年龄,相同就比较名字,都相同就认为是同一个,不加到集合里面

                return num2;
            }
        });
        // 创建学生对象
        Student s1 = new Student("ddd", 23);
        Student s2 = new Student("aa", 30);
        Student s3 = new Student("cc", 21);
        Student s4 = new Student("dd", 22);
        Student s5 = new Student("kk", 22);

        Student s6 = new Student("cxk", 22);
        Student s7 = new Student("cxk", 22);
        // 把学生添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        ts.add(s7);
        // 增强for遍历
        for (Student s : ts) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}
           

练习

题目:

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

思路:

  • 创建Set集合(没有重复的元素)
  • 创建随机数对象
  • 判断集合长度是否小于10,小于10则产生一个随机数,添加到集合,回到3继续循环
  • 遍历集合
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;

public class SetDemo {
    public static void main(String[] args) {
        // 创建Set集合,多态的写法
//        Set<Integer> set = new HashSet<>();     // HashSet集合显示的结果没有顺序,TreeSet集合显示的结果有顺序
        
        Set<Integer> set = new TreeSet<>();       // HashSet集合显示的结果没有顺序,TreeSet集合显示的结果有顺序
        // 创建随机数对象
        Random r = new Random();
        // 判断集合的长度是否小于10
        while (set.size()<10){
            // 产生一个随机数,添加到集合
            int number = r.nextInt(20)+1;
            set.add(number);
        }
        // 增强for遍历集合
        for(Integer i:set){
            System.out.println(i);
        }
    }
}
           

继续阅读