首页 > 其他分享 >集合进阶

集合进阶

时间:2024-01-22 10:46:10浏览次数:27  
标签:遍历 进阶 System println add 集合 out

集合进阶

集合体系结构

​ List -> ArrayList , LinkedList,

Collection <

​ Set -> HashSet(-> LinkedHashSet ) , TreeSet,

List系列集合 : 添加的元素是有序, 可重复, 有索引.

Set系列集合 : 添加的元素是无序, 不重复, 无索引.

单列集合 Collection

Collection是单列集合的顶级父接口, 它的功能是全部单列集合都可以继承使用的.

常用方法:

boolean add(E e) 把指定对象添加到集合

void clear() 清空集合中所有元素

boolead remove(E e) 删除指定对象

boolean contains(Object obj) 判断当前集合中是否包含指定对象

boolean isEmpty() 判断当前集合是否为空

int size() 返回集合中元素的个数 / 集合长度

注意点:

Collection是一个接口, 我们不能直接创建他的对象. 只能创建他的实现类对象ArrayList

/*boolean add(E e) 					把指定对象添加到集合
void clear()						清空集合中所有元素
boolead remove(E e)					删除指定对象
boolean contains(Object obj)		判断当前集合中是否包含指定对象
boolean isEmpty()					判断当前集合是否为空
int size() 							返回集合中元素的个数 / 集合长度*/
public class CollectionMethod {
    public static void main(String[] args) {
        //多态的形式创建对象是为了学习Collection中的方法
        Collection<String> coll = new ArrayList<>();

        //1.添加方法
        //返回值的细节: 往List系列集合中添加数据方法永远返回true,因为List系列集合允许元素重复.
        //             往Set集合中添加数据,如果当前添加的元素不存在,返回true,表示添加成功.
        //                               如果要添加的元素已经存在,返回false,添加失败.
        //                               因为Set系列的集合不允许元素重复.
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");

        System.out.println(coll);
        //2.清空方法
//        coll.clear();
//        System.out.println(coll);
        //3.删除方法
        //注意: 因为Collection里是集合中共性的方法,所以不能通过索引删除,只能通过元素的对象进行删除.
        //细节: 要删除的元素存在就删除成功,返回true. 否则就返回false.
        coll.remove("aaa");
        System.out.println(coll);
        //3.是否包含方法
        //细节:在底层依赖equals方法进行判断.
        //所以集合中是自定义对象,要使用contains方法判断是否存在,就要在Javabean类中重写equals方法.
        boolean bbb = coll.contains("bbb");
        System.out.println(bbb);
        //4.判断集合是否为空
        boolean empty = coll.isEmpty();
        System.out.println(empty);
        //获取集合长度
        System.out.println(coll.size());
    }
}

使用contains方法,重写equals方法

public static void main(String[] args) {
        //创建学生对象
        student s1 = new student("xiaoming", 8);
        student s2 = new student("dazhuang", 9);
        //s3和s2一样的属性
        student s3 = new student("dazhuang", 9);
        //创建集合
        ArrayList<student> list = new ArrayList<>();
        //添加对象
        list.add(s1);
        list.add(s2);
        //用contains方法判断
        //如果想要判断属性是否相同,就要重写javabean里的equals方法.否则Object类中的equals方法比较的是地址值.
        System.out.println(list.contains(s3));//不重写false, 重写后true
    }

//在Javabean类中重写equals方法
@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);
    }

Collection 遍历

迭代器遍历

迭代器在Java中的类是Iterator, 迭代器是集合专用的遍历方式, 不依赖索引.

获取迭代器:

Iterator iterator() 返回迭代器对象, 默认指向当前集合的0索引

方法:

boolean hasNext() 判断当前位置是否有元素, 有元素返回true, 无元素返回false

E next() 获取当前位置的元素, 并将迭代器对象移动到下一个位置

Collection<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        //迭代器遍历集合
        //获取迭代器对象,迭代器就好比一个箭头,默认指向集合的0索引处
        Iterator<String> it = list.iterator();
        //利用循环不断的获取集合中的每一个元素
        while (it.hasNext()) {
            //next方法做的两件事,1.获取元素  2.往下移动指针
            String next = it.next();
            System.out.print(next + " ");
        }

迭代器的注意点:

  1. 报错NoSuchElementException : 循环遍历后,迭代器的指针已经指向了最后没有元素的位置,再用next方法就会报错.

  2. 迭代器遍历完毕后,指针不会复位 : 如果我们要继续第二次遍历集合,只能再次获取一个新的迭代器对象.

  3. 循环中只能用一次next方法 : 循环中hasNext和next方法要配套使用.

  4. 迭代器遍历时, 不能用集合的方法进行增加或者删除 : 暂时不能添加元素, 只能用迭代器里的remove方法删除.

增强for遍历

  • 增强for底层就是迭代器, 为了简化迭代器代码书写的.
  • 它是JKD5以后出现的, 其内部原理就是Iterator迭代器.
  • 所有单列简化和数组才能用增强for遍历.

格式: for(元素的数据类型 变量名 : 数组或集合){ }

这里的变量名是一个第三方变量, 在循环过程中依次表示集合中的每一个数据.

修改增强for中的变量, 不会修改集合中原本的数据.

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

Lambda表达式遍历

得益于JKD8开始的新技术Lambda表达式, 提供了一种更简单,更直接的遍历集合方法.

default void forEach(Consumer<>); 结合Lambda遍历集合

Collection<String> coll = new ArrayList<>();
        //创建集合
        coll.add("aaa");
        coll.add("bbb");
        coll.add("ccc");
        //Lambda遍历
       /* coll.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        */
        //Lambda表达式简化写法
        coll.forEach(s -> System.out.println(s));

总结

  1. Collection是单列集合的顶层接口,所有方法被List和Set系列集合共享
  2. 常见成员方法:
    add、 clear、 remove、 contains、 isEmpty、 size
  3. 三种通用的遍历方式:
  • 迭代器:在遍历的过程中需要删除元素,请使用迭代器。
  • 增强for、Lambda:

仅仅想遍历,那么使用增强for或Lambda表达式。

List 集合常用方法和5种遍历方式

List集合的特有方法

  • Collection的方法List都继承了

  • List集合因为有索引,所以多了很多索引操作的方法。
    方法名称 说明
    void add(int index^E element) 在此集合中的指定位置插入指定的元素
    E remove(int index) 删除指定索引处的元素,返回被删除的元素
    E set(int index, E element) 修改指定索弓1处的元素,返回被修改的元素
    E get(int index) 返回指定索引处的元素

//List集合中删除方法(remove)的细节
        //创建集合
        List<Integer> list = new ArrayList<>();
        //添加元素
        list.add(1);
        list.add(2);
        list.add(3);
        //删除元素
        //请问,此时删除的是1元素还是1索引上的元素
        //在调用方法时,如果出现方法重载,优先调用实参和形参一致的那个方法
        //1是int类型数据,就默认为索引
        Integer remove = list.remove(1);
        System.out.println(remove);
        //如果要删除1元素,就要手动装箱
        Integer i = Integer.valueOf(1);
        System.out.println(list.remove(i));

List集合5种遍历方法

  1. 迭代器遍历
  2. 列表迭代器遍历------ 它是迭代器的子接口, 它添加了一个方法: 在遍历过程中可以添加元素.
  3. 增强for遍历
  4. Lambda表达式遍历
  5. 普通for循环遍历(list集合存在索引)
List<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        //1. 迭代器遍历
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }
        //2. 增强for遍历
        for (String s : list) {
            System.out.println(s);
        }
        //3. Lambda表达式遍历
        list.forEach(s -> System.out.println(s));
        //4. 普通for循环遍历(list集合存在索引)
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        System.out.println("列表迭代器遍历");
        //5. 列表迭代器遍历
        //创建列表迭代器对象
        ListIterator<String> li = list.listIterator();
        //遍历list集合并添加元素
        while (li.hasNext()){
            String next = li.next();
            if ("bb".equals(next)){
                li.add("**");
            }
        }
        System.out.println(list);

总结

迭代器遍历 --- 在遍历过程中要删除元素时使用

列表迭代器(List集合) --- 在遍历过程中要添加元素时使用

增强for / Lambda表达式 --- 仅仅想遍历时使用

普通for --- 如果遍历时想操作索引时使用

常见的数据结构

  1. 栈 : 后进先出,先进后出.
  2. 队列 : 先进先出, 后进后出.
  3. 数组 : 内存中的连续区域, 查询快, 增删慢.
  4. 链表 : 元素是游离的, 查询慢, 首位操作极快.

ArrayList 源码分析

大纲视图 : 快捷键 Alt + 7

成员搜索栏 : Ctrl + F12

LinkedList 源码分析

泛型深入

泛型类 : 当一个类中, 某个变量的数据类型不确定时,就可以定义带有泛型的类.

格式 : 修饰符 类名 { }

public class ArrayList<E>{ }//创建对象时,E就确定类型

此处的E可以理解为变量, 但是不是用来记录数据的, 而是记录数据类型, 可以写成 T , E , K , V等.

练习 : 自定义一个myArrayList泛型类, 存储各种类型的元素.

public class myArrayList<E> {
    Object[] obj = new Object[10];
    int size = 0;
    //E : 表示不确定的类型, 在该类的类名后已经定义过了
    //e : 形参的名字, 变量名
    //add
    public boolean add(E e) {
        obj[size] = e;
        size++;
        return true;
    }

    //get
    public E get(int index) {
        //把object类型的元素转换成E类型
        return (E) obj[index];
    }

    //toString
    public String toString() {
        return Arrays.toString(obj);
    }
}
//测试类
public static void main(String[] args) {
        myArrayList<String> list = new myArrayList<>();
        list.add("aaa");
        list.add("bbb");
        System.out.println(list.get(1));
        System.out.println(list.toString());

        myArrayList <Integer> list1 = new myArrayList<>();
        list1.add(123);
        list1.add(456);
        System.out.println(list1.get(0));
        System.out.println(list1.toString());
    }

泛型方法

方法形参类型不确定时, 有两种方案 :

方案1 : 使用类名后面定义的泛型. --- 所有的方法都能使用.

方案2 : 在方法声明上定义自己的泛型 --- 只能本方法使用.

格式 : 修饰符 <类型> 返回值类型 方法名(类型 变量名){ }

public <T> void show(T t){  }

此处的T可以理解为变量, 但是不是用来记录数据的, 而是记录数据类型, 可以写成 T , E , K , V等.

练习 : 定义一个工具类 ListUtil, 类中定义一个静态方法addAll, 用来添加多个元素.

//工具类
public class ListUtil {
    //私有化构造方法
    private ListUtil() {
    }

    //addAll方法 泛型要写在修饰符后面
    public static <E> void addAll(ArrayList<E> list, E e1, E e2, E e3) {
        list.add(e1);
        list.add(e2);
        list.add(e3);
    }

    //可变参数 E ... e 底层就是数组
    public static <E> void addAll(ArrayList<E> list, E... e) {
        for (E elemet : e) {
            list.add(elemet);
        }
    }
}
//测试类
public class test {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        ListUtil.addAll(list, "1", "2", "a", "b", "c");
        System.out.println(list);
    }
}       //[1, 2, a, b, c]

泛型接口

格式 : 修饰符 interface 接口名 <类型> { }

public interface List<E>{ }

重点 : 如何使用泛型接口.

方式1 : 实现类给出具体类型

public class myArrayList implements List<String>{}//一旦给出类型后,就确定了

方式2 : 实现类延续泛型, 创建对象时再确定.

public class myArrayList<E> implements List<E>{}//用于实现类还没有确定类型,在创建对象时再确定

泛型的继承和通配符

  • 泛型不具备继承性, 但是数据具备继承性.
  • 泛型写的什么类型, 就只能传递什么类型的数据.

利用泛型方法有一个小弊端, 就是它可以接受任意类型的数据, 有时候我们要对数据进行限制.

此时我们就可以使用泛型通配符.

*** ? 表示不确定的类型***

? extends E : 表示可以传递E和E的子类类型

*** ? super E : 表示可以传递E和E的父类类型***

**应用场景 : **

  1. 如果我们在定义类, 方法, 接口时, 如果类型不确定, 就可以定义泛型类, 泛型方法, 泛型接口.
  2. 如果类型不确定, 但是知道以后只能传递某一个继承体系, 就可以用泛型通配符.
  3. 泛型通配符就是限定类型的范围.

总结

  1. 什么是泛型?

JDK5引入的特性,可以在编译阶段约束操作的数据类型,并进行检查

  1. 泛型的好处?
  • 统一数据类型

  • 把运行时期的问题提前到了编译期间,避免了强制

​ 类型转换可能出现的异常,因为在编译阶段类型就能确定下来。

  1. 泛型的细节?
  • 泛型中不能写基本数据类型

  • 指定泛型的具体类型后,传递数据时,可以传入该类型和他的子类类型

  • 如果不写泛型,类型默认是Object

  1. 哪里定义泛型?
  • 泛型类:在类名后面定义泛型,创建该类对象的时候,确定类型
  • 泛型方法:在修饰符后面定义方法,调用该方法的时候,确定类型
  • 泛型接口:在接口名后面定义泛型,实现类确定类型,实现类延续泛型
  1. 泛型的继承和通配符
  • 泛型不具备继承性,但是数据具备继承性
  • 泛型的通配符:?

​ ? extend E

​ ? super E

  1. 使用场景
  • 定义类、方法、接口的时候,如果类型不确定,就可以定义泛型
  • 如果类型不确定,但是能知道是哪个继承体系中的,可以使用泛型的通配符

数据结构 (二叉树, 二叉查找数, 平衡二叉树)

二叉树

node - 节点的内部结构 :

父节点地址, 值, 左子节点地址, 右子节点地址 .

度 : 每一个节点的子节点数量.

二叉树中, 任意节点的度都<=2.

高 : 就是数的总层数.

根节点 : 最顶层的节点.

左子节点 : 左下方的节点.

右子节点 : 右下方的节点.

根节点的左/右的部分 : 叫根节点的左/右子树.

普通二叉树的弊端 :

普通二叉树查找非常麻烦, 必须要遍历, 二叉查找树查找的效率很高.

二叉查找树

特点 :

每一个节点上最多有两个子节点

任意节点左子树上的值都小于当前节点

任意节点右子树上的值都大于当前节点

添加节点的规则 :

小的存左边

大的存右边

一样的不存

二叉数的遍历方式 :

  1. 前序遍历 : 当前节点, 左子节点, 右子节点.
  2. 中序遍历 : 左子节点, 当前节点 , 右子节点. (最常用)
  3. 后序遍历 : 左子节点, 右子节点, 当前节点.
  4. 层序遍历 : 一层一层去遍历.

平衡二叉树

规则 : 任意节点左右子数高度差不超过1.

数据结构 (平衡二叉树的旋转机制)

规则 : 左旋和右旋

触发时机 : 当添加一个节点后, 该数不再是一个平衡二叉树时.

确定支点 : 从添加的节点开始, 不断的往父节点找不平衡的节点.

需要旋转的四种情况

  1. 左左 : 一次右旋
  2. 左右 : 先局部左旋, 再整体右旋
  3. 右右 : 一次左旋
  4. 右左 : 先局部右旋, 再整体左旋

数据结构(红黑树)

HashSet, LinkedHashSet详解

区别 :

List系列集合 : 添加的元素是有序, 可重复, 有索引.

Set系列集合 : 添加的元素是无序, 不重复, 无索引.

Set系列集合

  • 无序 : 存取顺序不一致
  • 不重复 : 可以去除重复
  • 无索引 : 没有带索引的方法, 所以不能使用普通for循环遍历, 也不能通过使用获取元素.

Set集合的实现类

  • HashSet : 无序, 不重复, 无索引.
  • LinkedHashSet : 有序, 不重复, 无索引.
  • TreeSet : 可排序, 不重复, 无索引.

Set接口中的方法基本上与Collection的API一致.

练习 : 存储字符串并遍历

利用Set系列集合, 添加字符串, 并使用多种方法遍历.

  1. 迭代器
  2. 增强fof
  3. Lambda表达式
public class SetDemo_1 {
    public static void main(String[] args) {
        /*练习 : 存储字符串并遍历
利用Set系列集合, 添加字符串, 并使用多种方法遍历.
1. 迭代器
2. 增强for
3. Lambda表达式*/
        Set<String> set = new HashSet<>();
        //添加元素
        //如果当元素第一次添加,那么可以添加成功,返回true
        //如果当元素第二次添加,那么就添加失败,返回false
        boolean r1 = set.add("aaa");//true
        boolean r2 = set.add("aaa");//false
        set.add("bbb");
        set.add("ccc");
        System.out.println(r1);
        System.out.println(r2);
        //1. 迭代器
        Iterator<String> it = set.iterator();
        while (it.hasNext()){
            String s = it.next();
            System.out.println(s);
        }
        System.out.println("-----");
        //2. 增强for
        for (String s : set) {
            System.out.print(s + " ");
        }
        System.out.println();
        System.out.println("-----");
        //3. Lambda表达式
        set.forEach(s -> System.out.println(s));
    }
}

hashSet底层原理

  • hashSet集合底层采取哈希表存储数据.
  • 哈希表是一种对于增删改查性能都较好的结构.

**哈希表的组成 **

  • JDK8之前 : 数组 + 链表
  • 从JDK8开始 : 数组 + 链表 + 红黑树

哈希值 : 对象的整数表现形式.

哈希值

  • 根据hashCode方法算出来的int类型的整数

  • 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算

  • 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈布值

对象的哈希值特点

  • 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的

  • 如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的

  • 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)

student1 s1 = new student1("abc", 10);
       student1 s2 = new student1("abc", 10);
        //重写hashCode方法后,不同的对象只要属性值相同,计算出的哈希值就是一样的
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
        //在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样.(哈希碰撞)
        System.out.println("abc".hashCode());//96354
        System.out.println("acD".hashCode());//96354

HashSet底层原理

① 创建一个默认长度16,默认加载因子0.75的数组,数组名table

② 根据元素的哈希值跟数组的长度计算出应存入的位置

③ 判断当前位置是否为null,如果是null直接存入

④ 如果位置不为null, 表示有元素,则调用equals方法比较属性值

⑤ 一样:不存 不一样:存入数组,形成链表

JDK8以前:新元素存入数组,老元素挂在新元素下面

JDK8以后:新元素直接挂在老元素下面

JDK8以后, 当链表长度超过8, 并且数组长度大于等于64时, 自动转换为红黑树.

如果集合中存储的自定义类型对象 , 必须要重写hashCode方法和equals方法.

HashSet三个问题

问题1: 为什么存和取的顺序不一样

链表的存和取的顺序就有可能不一样.

问题2: hashSet为什么没有索引

链表,红黑树都没有索引.

问题3: hashSet是利用什么机制保证数据去重的.

利用hashCode和equals方法.


练习 利用HashSet集合去除重复元素

需求:创建一个存储学生对象的集合,存储多个学生对象。

使用程序实现在控制台遍历该集合。

要求:学生对象的成员变量值相同,我们就认为是同一个对象

		npc n1 = new npc("a",1);
        npc n2 = new npc("b",2);
        npc n3 = new npc("c",3);
        //已经在javabean中重写了hashCode和equals方法
        //对象中属性相同的元素就是重复的元素,不会再次添加.
        npc n4 = new npc("a",1);
        //创建集合,要去重就用HashSet,不去重就用ArrayList.
        HashSet<npc> hs = new HashSet<>();
        //添加元素,添加失败返回false,
        System.out.println(hs.add(n1));//true
        System.out.println(hs.add(n2));//true
        System.out.println(hs.add(n3));//true
        System.out.println(hs.add(n4));//false
        //打印集合
        System.out.println(hs);

LinkedHashSet

  1. LinkedHashSet集合的特点和原理是怎么样的?
  • 有序、不重复、无索引
  • 底层基于哈希表,使用双链表记录添加顺序
  1. 在以后如果要数据去重,我们使用哪个?

默认使用HashSet

如果要求去重且存取有序,才使用LinkedHashSet


TreeSet

TreeSet的特点

  • 不重复, 无索引, 可排序.

  • 可排序 : 按照元素默认规则(从小到大)排序.

  • TreeSet底层基于红黑树数据结构实现排序, 增删改查性能都较好.

练习:

用TreeSet集合存储整数,并进行排序.

/*用TreeSet集合存储整数,并进行排序.*/
        TreeSet<Integer> ts = new TreeSet<>();
        ts.add(4);
        ts.add(3);
        ts.add(1);
        ts.add(2);
        ts.add(5);
        //遍历集合
        System.out.println(ts);
        //迭代器
        Iterator<Integer> it = ts.iterator();
        while (it.hasNext()) {
            Integer i = it.next();
            System.out.print(i + " ");
        }

        //增强for
        for (Integer t : ts) {
            System.out.print(t + " ");
        }

        //Lambda表达式
        ts.forEach(i -> System.out.print(i + " "));

TreeSet集合默认规则

  • 对于数值类型 : Integer , Double, 默认从小到大排序
  • 对于字符/字符串类型 : 按照字符在ASCII码表中的数字升序排序.
  • 比较字符串 : 从第一个字符开始比较,第一个字符一样再比较第二个,以此类推.

TreeSet的两种比较方法

方式1 : 默认排序 / 自然排序 - JavaBean类实现Comparable接口指定比较规则,重写里面的抽象方法compareTo().

compareTo方法中的this表示本次添加的元素, o表示已经在红黑树存在的元素.

return this.getAge() - o.getAge();

返回值如果是负数表示要添加的元素是小的, 存左边.

返回值如果是正数表示要添加的元素是大的, 存右边.

返回值如果是0表示要添加的元素已经存在, 舍弃.

hashCode 和equals方法跟"哈希表"有关.

TreeSet的底层是红黑树, 所以使用TreeSet集合比较元素时不需要重写这两个方法. 但是我们要指定他的排序规则.

练习 : 默认排序,实现Comparable接口,重写里面的抽象方法compareTo().

创建TreeSet集合,并添加3个学生对象

属性 : name age

按年龄排序, 年龄一样按姓名字母排序.年龄姓名一样就是同一个人.

public static void main(String[] args) {
        //创建对象
        person p1 = new person("zhangsan", 23);
        person p2 = new person("lisi", 24);
        person p3 = new person("wangwu", 25);
        //创建TreeSet集合
        TreeSet<person> ts = new TreeSet<>();
        //添加元素
        ts.add(p1);
        ts.add(p2);
        ts.add(p3);
        //打印集合
        System.out.println(ts);
    }
//----------------------------
//JavaBean类
//需要实现Comparable接口,我们比较的是person类型对象,
    //这里泛型就用<person>,最后重写CompareTo方法来指定排序规则.
class person implements Comparable<person> {
    private String name;
    private int age;
    ......
        //重写compareTo方法来指定排序规则
     @Override
    public int compareTo(person o) {
        //只比较年龄,从小到大升序
        System.out.println("---------");
        //显示调用方法进行比较的过程
        System.out.println("this:" + this);
        System.out.println("o:" + o);
        //指定排序规则
        return this.getAge() - o.getAge();
    }    
        

方式2 : 比较器排序 -- 创建TreeSet对象时, 传递比较器Comparator指定规则

使用原则 : 默认使用第一种方式, 如果第一种方式不能满足要求, 再用第二种方式.

练习 : 上面的练习,使用第二种方法比较器排序.

//默认排序
//创建集合
        TreeSet <String> ts = new TreeSet<>();
        //添加元素
        ts.add("df");
        ts.add("ab");
        ts.add("qwer");
        ts.add("c");
        //打印集合
        System.out.println(ts);
        //[ab, c, df, qwer],默认比较结果,按字母顺序.
//比较器排序---------------------------
//创建集合 参数传递比较器Complarator接口的实现类对象
        TreeSet <String> ts = new TreeSet<>(new Comparator<String>() {
            @Override   //它是函数式接口,可以改成Lambda表达式     
            public int compare(String o1, String o2) {
                //o1 : 当前要添加的元素
                //o2 : 已经在红黑树存在的元素
                //返回值 : 和上面的方法一样
                //先按长度排序
                int i = o1.length() - o2.length();
                //长度一样再按字母顺序排序,如果i等于0,
                //说明长度一样, 就调用compareTo默认方法比较.
                i = i == 0 ? o1.compareTo(o2) : i;
                return i;
            }//比较器比较的结果 [c, ab, df, qwer]
        });
        //添加元素
        ts.add("df");
        ts.add("ab");
        ts.add("qwer");
        ts.add("c");
        //打印集合
        System.out.println(ts);
        //[ab, c, df, qwer],默认比较结果,按字母顺序.

练习 : TreeSet对象排序练习

需求 :

  1. 创建5个学生对象
  2. 属性 : 姓名,年龄,语文成绩, 数学成绩, 英语成绩
  3. 按总分从高到底输出
  4. 如果总分一样, 按语文成绩排序
  5. 如果语文一样, 按数学成绩排序
  6. 如果数学一样, 按英语成绩排序
  7. 如果英语一样, 按年龄排序
  8. 如果年龄一样, 按姓名字母顺序
  9. 如果都一样, 认为是一个人, 不存.
public class TreeSetDemo01 {
    public static void main(String[] args) {
        //创建对象
        stu s1 = new stu("a", 10, 90, 90, 90);
        stu s2 = new stu("b", 11, 90, 90, 90);
        stu s3 = new stu("c", 12, 90, 90, 90);
        stu s4 = new stu("d", 13, 100, 80, 90);
        stu s5 = new stu("e", 14, 70, 100, 100);
        //创建集合
        TreeSet<stu> ts = new TreeSet<>();

        //添加对象
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        //遍历集合
        for (stu t : ts) {
            System.out.println(t);
        }

    }
}
//比较对象属性时,默认使用第一种方式, 如果第一种方式不能满足要求, 再用第二种方式.
//这里就用第一种方式: 默认排序 / 自然排序  -  JavaBean类实现Comparable接口指定比较规则,
class stu implements Comparable<stu> {
    private String name;
    private int age;
    private int chinese;
    private int math;
    private int english;

    public stu() {
    }
//... ...
    //加入总分的显示
    public String toString() {
        int score = this.getEnglish() + this.getMath() + this.getChinese();
        return "stu{name = " + name + ", age = " + age + ", chinese = " + chinese + ", " +
                "math = " + math + ", english = " + english + ", TScore = " + score + "}";
    }
	//排序规则
    @Override
    public int compareTo(stu o) {
        //按总分从高到底输出
        //4. 如果总分一样, 按语文成绩排序
        int s1 = this.getChinese() + this.getMath() + this.getEnglish();
        int s2 = o.getChinese() + o.getMath() + o.getEnglish();
        int i = s1 - s2;
        i = i == 0 ? this.getChinese() - o.getChinese() : i;
        //5. 如果语文一样, 按数学成绩排序
        i = i == 0 ? this.getMath() - o.getMath() : i;
        //6. 如果数学一样, 按英语成绩排序(不用写)
        //7. 如果英语一样, 按年龄排序
        i = i == 0 ? this.getAge() - o.getAge() : i;
        //8. 如果年龄一样, 按姓名字母顺序
        i = i == 0 ? this.getName().compareTo(o.getName()) : i;
        //9. 如果都一样, 认为是一个人, 不存.
        return i;
    }
}
//----------------------
stu{name = e, age = 14, chinese = 70, math = 100, english = 100, TScore = 270}
stu{name = a, age = 10, chinese = 90, math = 90, english = 90, TScore = 270}
stu{name = b, age = 11, chinese = 90, math = 90, english = 90, TScore = 270}
stu{name = c, age = 12, chinese = 90, math = 90, english = 90, TScore = 270}
stu{name = d, age = 13, chinese = 100, math = 80, english = 90, TScore = 270}

总结

  1. TreeSet集合的特点是怎么样的?
  • 可排序、不重复、无索引
  • 底层基于红黑树实现排序,增删改查性能较好
  1. TreeSet集合自定义排序规则有几种方式
  • 方式一:javabean类实现Comparable接口,指定比较规则 (Java规定的方式)
  • 方式二:创建集合时,自定义Comparator比较器对象,指定比较规则(自定义方式)

选择哪一种方法?

  • d默认使用第一种方式, 如果第一种方式不能满足要求, 再用第二种方式. 比如说:字符串的排序规则Java已经规定好了,是按照字母顺序排. 但我想要按照字母数量排序.或者整数排序默认是升序,但是我要降序排.以上的需求只能用第二种方式排序了.

  • 当程序中方式一和方式二都存在时,是以方式二为准的.

  1. 方法返回值的特点
  • 负数:表示当前要添加的元素是小的,存左边
  • 正数:表示当前要添加的元素是大的,存右边
  • 0:表示当前要添加的元素已经存在,舍弃

单列集合Collection 总结

使用场景

  1. 如果想要集合中的元素可重复
  • ArrayList集合,基于数组的。(用的最多)
  1. 如果想要集合中的元素可重复,而且当前的增删操作明显多于查询
  • LinkedList集合,基于链表的。
  1. 如果想对集合中的元素去重
  • HashSet集合,基于哈希表的。(用的最多)
  1. 如果想对集合中的元素去重,而且保证存取顺序
  • LinkedHashSet集合,基于哈希表和双链表,效率低于HashSet
  1. 如果想对集合中的元素进行排序
  • TreeSet集合,基于红黑树。后续也可以用List集合实现排序。

标签:遍历,进阶,System,println,add,集合,out
From: https://www.cnblogs.com/lg369/p/17979479

相关文章

  • [转]Java Stream API进阶篇
    原文地址:JavaStreamAPI进阶篇-CarpenterLee-博客园本文github地址上一节介绍了部分Stream常见接口方法,理解起来并不困难,但Stream的用法不止于此,本节我们将仍然以Stream为例,介绍流的规约操作。规约操作(reductionoperation)又被称作折叠操作(fold),是通过某个连接动作将所有......
  • 传入一个List集合,返回分页好的数据
    传入一个List集合,返回分页好的数据。定义分页信息类:packagecom.cn.common;importjava.util.List;publicclassCommonPage<T>{privateintcurrentPage;privateinttotalPage;privateintpageSize;privatejava.util.List<T>list;publ......
  • 4、ceph-crush进阶
    一、CephCrush进阶ceph集群中由mon服务器维护的的五种运行图:Monitormap #监视器运行图OSDmap#OSD运行图PGmap #PG运行图Crushmap #(Controllersreplicationunderscalablehashing#可控的、可复制的、可伸缩的一致性hash算法。crush运行图,当新建存储池时会基......
  • 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象
    快速对比两个list数据集合此文引用csdn:https://blog.csdn.net/Zhu_daye/article/details/104798410小批量、快速对比两个list数据集合usingSystem.Linq.Expressions;Main();voidMain(){//对比源集合varsource=GenerateStudent(1,10000,1000);//......
  • 二叉树面试题进阶
    二叉树面试题进阶1.二维数组存储层序遍历结果难点: 如何存储每一层的节点?根据队列节点的个数判断每一层classSolution{publicList<List<Integer>>levelOrder(TreeNoderoot){List<List<Integer>>retList=newArrayList<>();if(root==nu......
  • shiro实现用户踢出,在线用户列表展示功能,包含常见踩坑集合、代码下载
    功能描述:用户a登录了s账号,接着用户b也登录了s账号,此时用户a将被踢出。一个账号只能一个人登录,被别人登录了,那么你就要被踢下线。本文目录shiro认证与授权理解实现需求核心以下是实现shiro用户踢出KickOutListener(登录成功后加入业务逻辑)kickOutFilter(进入controller的初级验证)配置......
  • 代码之外:工程师的成长进阶秘籍
    程序员只懂技术能行吗? 为什么说技术人员“说”和“写”总得擅长一个? 你以为的“关注结果”是真的结果吗? 从一线工程师跃升团队管理者一共分几步? 在不断变化的职场环境中,技术人如何保持竞争力并实现自我增值,是摆在每个人面前的挑战。无论是一线工程师还是技术管理者,如......
  • trick 集合
    trick集合1.基础判断是否\(\foralli\)有\(x<a_i\):转化为是否\(x<\min(a_i)\)。大于类似。P9868&题解,ABC223F&题解括号序列:是括号序列的条件:总共的左括号和右括号数量相等;任意前缀的左括号数量\(\ge\)右括号数量。若将序列中左括号看作......
  • TS进阶1
    //1、函数重载functionhello(name:string):stringfunctionhello(age:number):stringfunctionhello(value:string|number):string{if(typeofvalue==='string'){return'你好,我的名字是'+value}elseif(typeofvalue==='......
  • 鸿蒙生态游戏揭开进阶新篇章,25家游戏伙伴参加合作仪式
    1月18日,鸿蒙千帆启航仪式在深圳召开,华为宣布HarmonyOSNEXT鸿蒙星河版开发者预览面向开发者开放申请,并公布鸿蒙生态最新进展:鸿蒙生态设备数量仅历时5个月即从7亿增长至8亿,千行百业实现万物互联,将打开万亿级产业新蓝海。游卡网络、紫龙游戏、雷霆游戏、巨人网络、中旭未来、游酷盛......