惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
Microsoft Security Blog
Microsoft Security Blog
Recent Announcements
Recent Announcements
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
罗磊的独立博客
腾讯CDC
云风的 BLOG
云风的 BLOG
月光博客
月光博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
宝玉的分享
宝玉的分享
U
Unit 42
I
InfoQ
D
DataBreaches.Net
Blog — PlanetScale
Blog — PlanetScale
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
V2EX
美团技术团队
IT之家
IT之家
Stack Overflow Blog
Stack Overflow Blog
F
Fortinet All Blogs
GbyAI
GbyAI
S
SegmentFault 最新的问题

风萧古道 - 勤学苦练,年复一年

AI 编程时代,想象力成了最被奖励的能力 游戏服务器开发经验(五)应对复杂需求 沉浸式体验东汉末年生活 - 《真三国无双 起源》玩后感 怒其不争!致2025年HLTV的Top18-NiKo Linux家用服务器维护指南 游戏服务器开发经验(四)避免写Bug的习惯、技巧和心态 游戏服务器开发经验(三)线上维护 游戏服务器开发经验(二)避免内存泄露 游戏服务器开发经验(一)道具防刷 35岁找不到工作,绝对不会是软件开发人员的结局 用爱发电项目开发两个月的心得体会 以魏延“子午谷奇谋”讨论软件需求可行性问题 MQTT协议中可变长度的具体计算方式(有计算过程解析) 关于游戏服务器配置表功能的探讨 Java并发编程中上锁的几种方式 如何用C++分割一个字符串? CSAPP第二章-信息的表示与处理 我的自我介绍 Windows XP虚拟机中文版无需激活下载 Linux C++ Socket实战 传统软件服务器与游戏服务器架构区别 独立个人项目开发心得 - 任务切分、挑战性、实用性和半途而废 使用Python实现简单UDP Ping 使用Python开发一个简单的web服务器 Kotlin手动实现一个最简单的哈希表 Kotlin实现二叉堆、大顶堆、优先级队列 搭建Spark实战环境(3台linux虚拟机集群)(一)样板机的搭建 Springboot操作MongoDB,包括增改查及复杂操作 Unison在Linux下的安装与使用 Java实现类似WINSCP访问远程Linux服务器,执行命令、上传文件、下载文件
Java TreeSet的一些用法和特性
JonathanLin · 2022-06-18 · via 风萧古道 - 勤学苦练,年复一年

就是一些例子啦。

先看一个例子(kotlin实现)

import java.util.TreeSet

/**
 * 定义一个用于测试TreeSet集合的结构
 * 用TreeSet进行排名
 * id: 玩家id
 * score: 玩家得分
 */
data class PlayerScore(var id: Int, var score: Int): Comparable<PlayerScore> {
    override fun compareTo(other: PlayerScore): Int {
        return if (score > other.score) {
            1
        } else if (score < other.score) {
            -1
        } else {
            0
        }
    }

}


fun main() {
    //创建一个TreeSet
    val treeSet: TreeSet<PlayerScore> = TreeSet()
    //创建3个PlayerScore,其中对id为101的对象存一个引用
    //将3个PlayerScore装入set中
    val id101Obj = PlayerScore(101,100)
    //按不同的顺序加入TreeSet
    treeSet.add(PlayerScore(102,200))
    treeSet.add(id101Obj)
    treeSet.add(PlayerScore(103,300))

    println("按不同的顺序加入TreeSet,按score从小到大输出:")
    showTreeSet(treeSet)

    println("再加入重复id,但分数不同的对象,按score从小到大输出:")
    treeSet.add(PlayerScore(101,500))
    showTreeSet(treeSet)

    println("直接修改id为101的对象的分数为400,按score从小到大输出:")
    id101Obj.score = 400
    showTreeSet(treeSet)

}

private fun showTreeSet(treeSet: TreeSet<PlayerScore>) {
    treeSet.forEachIndexed { index, s ->
        println("排名:${index+1} 分数:${s.score} id:${s.id}")
    }
    println()
}

运行结果:

按不同的顺序加入TreeSet,按score从小到大输出:
排名:1 分数:100 id:101
排名:2 分数:200 id:102
排名:3 分数:300 id:103

再加入重复id,但分数不同的对象,按score从小到大输出:
排名:1 分数:100 id:101
排名:2 分数:200 id:102
排名:3 分数:300 id:103
排名:4 分数:500 id:101

直接修改id为101的对象的分数为400,按score从小到大输出:
排名:1 分数:400 id:101
排名:2 分数:200 id:102
排名:3 分数:300 id:103
排名:4 分数:500 id:101

分析

首先,PlayerScore继承Comparable接口,实现了从小到大排序。在最开始只有3条数据时,按照不同的顺序插入TreeSet,可以正常的完成排序。

之后玩家101的分数发生改变,需要重新给玩家进行排名,我们分别通过两种方式进行操作:

  1. 通过id101Obj的引用来对这个对象的分数进行修改;
  2. 通过再往TreeSet中加入一个id为101,分数不同于之前的分数的对象。

结果: 第一种方式:玩家的积分被修改了,但是排名依旧为第1; 第二种方式:虽然积分正常排序,但是有两个id为101的对象。

所以如果我们的玩家积分发生改变的时候,要如何才能正常排序呢?

可以对某个对象先从TreeSet中删除,再往TreeSet中插入。这是因为TreeSet的底层是红黑树,在删除和插入的时候,会对树结构进行调整。 代码如下:

    //创建一个TreeSet
    val treeSet: TreeSet<PlayerScore> = TreeSet()
    //创建3个PlayerScore,其中对id为101的对象存一个引用
    //将3个PlayerScore装入set中
    val id101Obj = PlayerScore(101,100)
    //按不同的顺序加入TreeSet
    treeSet.add(PlayerScore(102,200))
    treeSet.add(id101Obj)
    treeSet.add(PlayerScore(103,300))

    println("按不同的顺序加入TreeSet,按score从小到大输出:")
    showTreeSet(treeSet)

    treeSet.remove(id101Obj)
    id101Obj.score = 400
    treeSet.add(id101Obj)
    println("先将id101Obj从TreeSet中删除,修改值,然后再插入TreeSet中,按score从小到大输出:")
    showTreeSet(treeSet)

结果如下:

按不同的顺序加入TreeSet,按score从小到大输出:
排名:1 分数:100 id:101
排名:2 分数:200 id:102
排名:3 分数:300 id:103

先将id101Obj从TreeSet中删除,修改值,然后再插入TreeSet中,按score从小到大输出:
排名:1 分数:200 id:102
排名:2 分数:300 id:103
排名:3 分数:400 id:101

更进一步

先删后加的方式是可以完成排序,但是TreeSet本身是线程不安全的。如果有多个线程都要更新玩家积分和排名,那么大概率会存在问题。 TreeSet虽然可以边插边排序,但是它只允许一次插入,插入之后积分的修改并不会修改排序。

对于排行榜上的排名经常变动的情况,用TreeSet是不太合理的。

另一种排行榜实现

本来选用TreeSet,是因为TreeSet拥有“边插入边排序”的特点,但在排行榜需要更新时并不方便。 那么换一种思路,如果只用变长的ArrayList来存储玩家积分对象,同样是在插入/更新/删除时进行排序?

先插入/更新/删除,对ArrayList内的数据进行修改,然后进行排序。