背景:在平时的开发中,我们时常会遇到下列场景

  1. 公司的组织架构的数据存储与展示
  2. 文件夹层级的数据存储与展示
  3. 评论系统中,父评论与诸多子评论的数据存储与展示
  4. ......

对于这种有层级的结构化数据,就像是一棵一样。在关系型数据库中,通常将一个个的节点信息存储到表中,通过一个字段(例如,pid),指向其父节点。而在数据展示的时候,我们又希望它是呈现层级的,例如:

id  name        pid
1 总公司 -1
2 上海分公司 1
3 浙江分公司 1
4 闵行区分公司 2
5 嘉兴分公司 3 {
"id": 1,
"name": "总公司",
"pid": -1,
"branch":
[
{
"id": 2,
"name": "上海分公司",
"pid": 1,
"branch":
[
{
"id": 4,
"name": "闵行区分公司",
"pid": 2,
"branch":
[]
}
]
},
{
"id": 3,
"name": "浙江分公司",
"pid": 1,
"branch":
[
{
"id": 5,
"name": "嘉兴分公司",
"pid": 3,
"branch":
[]
}
]
}
]
}

所以,本文的主要内容就是提供几种方案,实现这两类数据的转换方式。

内容导览


存储树的表结构

如何在一张数据库表中表示一颗树结构中的所有节点信息,这里有一个示例:

DROP TABLE IF EXISTS zj_city;
CREATE TABLE zj_city (
id BIGINT NOT NULL AUTO_INCREMENT,
name VARCHAR(50) COMMENT '节点名称',
pid int NOT NULL COMMENT '父节点', create_time DATETIME DEFAULT now() COMMENT '创建时间: 年-月-日 时:分:秒',
update_time DATETIME DEFAULT now() ON UPDATE now() COMMENT '更新时间',
is_deleted BIT DEFAULT 0 COMMENT '是否删除:0-false-未删除;1-true-已删除',
PRIMARY KEY (id)
)COMMENT '浙江城市'; INSERT INTO zj_city(name, pid) VALUES
('浙江省',0),
('金华市',1),
('嘉兴市',1),
('杭州市',1),
('宁波市',1); INSERT INTO zj_city(name, pid) VALUES
('下城区',4),
('钱塘区',4),
('西湖区',4),
('上城区',4); INSERT INTO zj_city(name, pid) VALUES
('南湖区',3),
('秀洲区',3),
('桐乡市',3),
('平湖市',3),
('海宁市',3); INSERT INTO zj_city(name, pid) VALUES
('梧桐街道',12),
('凤鸣街道',12),
('龙翔街道',12),
('崇福镇',12),
('乌镇镇',12),
('高桥镇',12),
('河山镇',12),
('濮院镇',12); SELECT * from zj_city;

扁平List转树形List

应用场景

  • 公司组织结构
  • 省市级
  • 评论系统中,父评论与诸多子评论
  • 其他层级展示的数据

双层for

主要思想:外层循环-找父节点;内层循环-找子节点;因为每个元素都会找一遍,所有最终得到完整的树

import com.alibaba.fastjson.JSON;
import com.ks.boot.entity.CityEntity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import java.util.ArrayList;
import java.util.List; public class TreeListDemo {
List<CityEntity> cityEntities;
/**
* id name pid
* 1 浙江 0
* 2 杭州 1
* 3 嘉兴 1
* 4 南湖 3
* 5 桐乡 3
* 6 余杭 2
* 7 西湖 2
* 8 云南 0
* 9 昆明 8
* 10 昭通 8
*/
@BeforeEach
public void init() {
cityEntities = JSON.parseArray("[{\"id\":1,\"name\":\"浙江\",\"pid\":0},\n" +
"{\"id\":2,\"name\":\"杭州\",\"pid\":1},\n" +
"{\"id\":3,\"name\":\"嘉兴\",\"pid\":1},\n" +
"{\"id\":4,\"name\":\"南湖\",\"pid\":3},\n" +
"{\"id\":5,\"name\":\"桐乡\",\"pid\":3},\n" +
"{\"id\":6,\"name\":\"余杭\",\"pid\":2},\n" +
"{\"id\":7,\"name\":\"西湖\",\"pid\":2},\n" +
"{\"id\":8,\"name\":\"云南\",\"pid\":0},\n" +
"{\"id\":9,\"name\":\"昆明\",\"pid\":8},\n" +
"{\"id\":10,\"name\":\"昭通\",\"pid\":8}]", CityEntity.class);
} @Test
public void testList2Tree() {
List<CityEntity> resultTree = list2Tree(this.cityEntities);
System.out.println(JSON.toJSONString(resultTree));
} /**
* 双层for,List转Tree
* 主要思想:外层循环-找父节点;内层循环-找子节点;因为每个元素都会找一遍,所有最终得到完整的树
* 时间复杂度:N^2;空间复杂度:N
*/
public List<CityEntity> list2Tree(List<CityEntity> cityEntities) {
List<CityEntity> resultCities = new ArrayList<>();
for (CityEntity city : cityEntities) {
if(0 == city.getPid()) { //根节点、顶级节点,直接放入最终返回结果的List
resultCities.add(city);
}
for (CityEntity curCity : cityEntities) { //根据当前city找它的子节点
if(city.getId().equals(curCity.getPid())) {
if(city.getSubCityList() == null) { //还没有任何子节点,new一个空的放进去
city.setSubCityList(new ArrayList<>());
}
city.getSubCityList().add(curCity);
}
}
} return resultCities;
}
} public class CityEntity {
private Long id;
private String name;
private Long pid; private List<CityEntity> subCityList; getter/setter
}

递归

主要思想:获取所有根节点、顶级节点,再从List中查找根节点的子节点;

import com.alibaba.fastjson.JSON;
import com.ks.boot.entity.CityEntity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import java.util.ArrayList;
import java.util.List; public class TreeListDemo {
List<CityEntity> cityEntities;
/**
* id name pid
* 1 浙江 0
* 2 杭州 1
* 3 嘉兴 1
* 4 南湖 3
* 5 桐乡 3
* 6 余杭 2
* 7 西湖 2
* 8 云南 0
* 9 昆明 8
* 10 昭通 8
*/
@BeforeEach
public void init() {
cityEntities = JSON.parseArray("[{\"id\":1,\"name\":\"浙江\",\"pid\":0},\n" +
"{\"id\":2,\"name\":\"杭州\",\"pid\":1},\n" +
"{\"id\":3,\"name\":\"嘉兴\",\"pid\":1},\n" +
"{\"id\":4,\"name\":\"南湖\",\"pid\":3},\n" +
"{\"id\":5,\"name\":\"桐乡\",\"pid\":3},\n" +
"{\"id\":6,\"name\":\"余杭\",\"pid\":2},\n" +
"{\"id\":7,\"name\":\"西湖\",\"pid\":2},\n" +
"{\"id\":8,\"name\":\"云南\",\"pid\":0},\n" +
"{\"id\":9,\"name\":\"昆明\",\"pid\":8},\n" +
"{\"id\":10,\"name\":\"昭通\",\"pid\":8}]", CityEntity.class);
} /**
* 递归,List转Tree
* 主要思想:获取所有根节点、顶级节点,再从List中查找根节点的子节点;
* 时间复杂度:N*(1+N)/2,O(N^2),因为递归方法中,最好是List的第一元素就是要找的子节点,最坏是
* List的最后一个元素是子节点
*/
@Test
public void testList2Tree() {
List<CityEntity> resultCities = new ArrayList<>();
for (CityEntity city : cityEntities) {
if(0 == city.getPid()) { //获取所有根节点、顶级节点,再根据根节点进行递归
CityEntity topCity = findChild(cityEntities, city); //此时的topCity已经包含它的所有子节点
resultCities.add(topCity);
}
} System.out.println(JSON.toJSONString(resultCities));
} /**
*
* @param cityEntities 在那个里面找
* @param curCity 找谁的子节点
* @return curCity的子节点
*/
public CityEntity findChild(List<CityEntity> cityEntities, CityEntity curCity) {
for (CityEntity city : cityEntities) {
if(curCity.getId().equals(city.getPid())) {
if(null == curCity.getSubCityList()) {
curCity.setSubCityList(new ArrayList<>());
}
CityEntity subChild = findChild(cityEntities, city); //每次递归,都从头开始查找有没有city的子节点
curCity.getSubCityList().add(subChild);
}
}
return curCity;
} } public class CityEntity {
private Long id;
private String name;
private Long pid; private List<CityEntity> subCityList; getter/setter
}

转换为Map

主要思想

  • 在双层for的解法中,由于内层for也需要遍历以便List,造成时间复杂度上身为平方级
  • 如果内层for不需要遍历完整的List,而是事先预处理到Map中,寻找时直接从Map中获取,则时间复杂度降为LogN
import com.alibaba.fastjson2.JSON;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; public class TreeListDemo {
List<CityEntity> cityEntities;
/**
* id name pid
* 1 浙江 0
* 2 杭州 1
* 3 嘉兴 1
* 4 南湖 3
* 5 桐乡 3
* 6 余杭 2
* 7 西湖 2
* 8 云南 0
* 9 昆明 8
* 10 昭通 8
*/
@BeforeEach
public void init() {
cityEntities = JSON.parseArray("[{\"id\":1,\"name\":\"浙江\",\"pid\":0},\n" +
"{\"id\":2,\"name\":\"杭州\",\"pid\":1},\n" +
"{\"id\":3,\"name\":\"嘉兴\",\"pid\":1},\n" +
"{\"id\":4,\"name\":\"南湖\",\"pid\":3},\n" +
"{\"id\":5,\"name\":\"桐乡\",\"pid\":3},\n" +
"{\"id\":6,\"name\":\"余杭\",\"pid\":2},\n" +
"{\"id\":7,\"name\":\"西湖\",\"pid\":2},\n" +
"{\"id\":8,\"name\":\"云南\",\"pid\":0},\n" +
"{\"id\":9,\"name\":\"昆明\",\"pid\":8},\n" +
"{\"id\":10,\"name\":\"昭通\",\"pid\":8}]", CityEntity.class);
} /**
* 在双层for的解法中,由于内层for也需要遍历以便List,造成时间复杂度上身为平方级
* 如果内层for不需要遍历完整的List,而是事先预处理到Map中,寻找时直接从Map中获取,则时间复杂度降为LogN
*/
@Test
public void list2tree() {
//收集每个PID下的节点为Map
Map<Long, List<CityEntity>> cityMapByPid = cityEntities.stream().collect(Collectors.groupingBy(CityEntity::getPid)); List<CityEntity> resultCityList = new ArrayList<>();
for (CityEntity city : cityEntities) {
if(0 == city.getPid()) { //根节点、顶点
resultCityList.add(city);
} List<CityEntity> citiesByPid = cityMapByPid.get(city.getId());
if(null != citiesByPid && citiesByPid.size() > 0) { //有子节点
if(null == city.getSubCityList()) {
city.setSubCityList(new ArrayList<>());
}
city.getSubCityList().addAll(citiesByPid); //塞入
}
} System.out.println(JSON.toJSONString(resultCityList));
} /**
* 简化写法:在收集到Map时,对于没有子节点的节点,创建一个空的塞入到Map中
*/
@Test
public void list2tree4Simple() {
List<CityEntity> resultCityList = new ArrayList<>(); //保存每个PID下的子节点
Map<Long, List<CityEntity>> cityMapByPid = new HashMap<>();
for (CityEntity city : cityEntities) { //收集每个PID下的子节点
//获取当前PID对应的子节点List,如果没有则创建一个空的List塞入
//这个设计得很巧妙
List<CityEntity> children = cityMapByPid.getOrDefault(city.getPid(), new ArrayList<>());
children.add(city); //插入当前元素
cityMapByPid.put(city.getPid(), children);
} for (CityEntity city : cityEntities) {
if(0 == city.getPid()) { //根节点、顶点
resultCityList.add(city);
}
city.setSubCityList(cityMapByPid.get(city.getId()));
} System.out.println(JSON.toJSONString(resultCityList));
}
}

主要思想

  • 收集根节点、顶级节点,存入resultList,并且压栈
  • 循环出栈,栈元素Cur
    • 找Cur的所有子元素为child
    • 如果child不为空,则再压入栈中。这一步的目的是,再一次找child的子元素
import com.alibaba.fastjson2.JSON;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import java.util.*;
import java.util.stream.Collectors; public class TreeListDemo {
List<CityEntity> cityEntities;
/**
* id name pid
* 1 浙江 0
* 2 杭州 1
* 3 嘉兴 1
* 4 南湖 3
* 5 桐乡 3
* 6 余杭 2
* 7 西湖 2
* 8 云南 0
* 9 昆明 8
* 10 昭通 8
*/
@BeforeEach
public void init() {
cityEntities = JSON.parseArray("[{\"id\":1,\"name\":\"浙江\",\"pid\":0},\n" +
"{\"id\":2,\"name\":\"杭州\",\"pid\":1},\n" +
"{\"id\":3,\"name\":\"嘉兴\",\"pid\":1},\n" +
"{\"id\":4,\"name\":\"南湖\",\"pid\":3},\n" +
"{\"id\":5,\"name\":\"桐乡\",\"pid\":3},\n" +
"{\"id\":6,\"name\":\"余杭\",\"pid\":2},\n" +
"{\"id\":7,\"name\":\"西湖\",\"pid\":2},\n" +
"{\"id\":8,\"name\":\"云南\",\"pid\":0},\n" +
"{\"id\":9,\"name\":\"昆明\",\"pid\":8},\n" +
"{\"id\":10,\"name\":\"昭通\",\"pid\":8}]", CityEntity.class);
} /**
* 主要思想:
* 收集根节点、顶级节点,存入resultList,并且压栈
* 循环出栈,栈元素Cur
* 找Cur的所有子元素为child
* 如果child不为空,则再压入栈中。这一步的目的是,再一次找child的子元素
* 时间复杂度:N(过滤出所有跟节点) + 常数(出栈) * N(遍历List找当前节点的子元素)
*/
@Test
public void list2tree() {
List<CityEntity> resultCityList = new ArrayList<>(); Stack<CityEntity> stack = new Stack<>();
resultCityList = cityEntities.stream().filter(ele -> 0 == ele.getPid()).collect(Collectors.toList());
stack.addAll(resultCityList); //根节点、顶点入栈 while(!stack.isEmpty()) {
CityEntity curCity = stack.pop();
List<CityEntity> child = cityEntities.stream().filter(ele -> curCity.getId().equals(ele.getPid())).collect(Collectors.toList());
if(!child.isEmpty()) { //这一步处理的原因是:当没有子元素,不显示该个字段。流处理后没有元素只会返回空List,不会返回null
curCity.setSubCityList(child);
}
if(!child.isEmpty()) {
stack.addAll(child);
}
} System.out.println(JSON.toJSONString(resultCityList));
}
}

树形List转扁平List

递归

主要思想:遍历树节点,一个树节点如果有子树,则再次递归此子树,直到没有子树为止

import com.alibaba.fastjson2.JSON;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import java.util.ArrayList;
import java.util.List; /**
* id name pid
* 1 浙江 0
* 2 杭州 1
* 3 嘉兴 1
* 4 南湖 3
* 5 桐乡 3
* 6 余杭 2
* 7 西湖 2
* 8 云南 0
* 9 昆明 8
* 10 昭通 8
*/
public class ListTreeDemo {
List<CityEntity> treeList; @BeforeEach
public void init() {
treeList = JSON.parseArray("[{\"id\":1,\"name\":\"浙江\",\"pid\":0,\"subCityList\":[" +
"{\"id\":2,\"name\":\"杭州\",\"pid\":1,\"subCityList\":[{\"id\":6,\"name\":\"余杭\",\"pid\":2},{\"id\":7,\"name\":\"西湖\",\"pid\":2}]}," +
"{\"id\":3,\"name\":\"嘉兴\",\"pid\":1,\"subCityList\":[{\"id\":4,\"name\":\"南湖\",\"pid\":3},{\"id\":5,\"name\":\"桐乡\",\"pid\":3}]}]}," +
"{\"id\":8,\"name\":\"云南\",\"pid\":0,\"subCityList\":[{\"id\":9,\"name\":\"昆明\",\"pid\":8},{\"id\":10,\"name\":\"昭通\",\"pid\":8}]}]", CityEntity.class);
} @Test
public void tree2list() {
List<CityEntity> resList = new ArrayList<>(); //这一层for的目的是:遍历根节点
for (CityEntity city : treeList) {
reversion(city,resList);
}
System.out.println(JSON.toJSONString(resList));
}
public void reversion(CityEntity curNode, List<CityEntity> resList) {
resList.add(beanCopy(curNode)); List<CityEntity> subCityList = curNode.getSubCityList();
if(subCityList != null && !subCityList.isEmpty()) {
for (CityEntity city : subCityList) { //递归寻找子节点的子节点们
reversion(city, resList);
}
} //递归的出口就是subCityList为null或者empty
} private CityEntity beanCopy(CityEntity source) {
CityEntity res = new CityEntity();
res.setId(source.getId());
res.setName(source.getName());
res.setPid(source.getPid());
return res;
}
}

主要思想

  1. 依次遍历树形List,当前节点为Cur

    1. 将Cur收集到某个存储结果的List
    2. 如果Cur有子树,压入某个栈中
  2. 依次弹出栈元素,当前弹出的元素为StackSubTree
    1. 如果StackSubTree还有子树,继续压栈
    2. 如果StackSubTree没有子树,则放入结果List
import com.alibaba.fastjson2.JSON;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import java.util.ArrayList;
import java.util.List;
import java.util.Stack; /**
* id name pid
* 1 浙江 0
* 2 杭州 1
* 3 嘉兴 1
* 4 南湖 3
* 5 桐乡 3
* 6 余杭 2
* 7 西湖 2
* 8 云南 0
* 9 昆明 8
* 10 昭通 8
*/
public class ListTreeDemo {
List<CityEntity> treeList; @BeforeEach
public void init() {
treeList = JSON.parseArray("[{\"id\":1,\"name\":\"浙江\",\"pid\":0,\"subCityList\":[" +
"{\"id\":2,\"name\":\"杭州\",\"pid\":1,\"subCityList\":[{\"id\":6,\"name\":\"余杭\",\"pid\":2},{\"id\":7,\"name\":\"西湖\",\"pid\":2}]}," +
"{\"id\":3,\"name\":\"嘉兴\",\"pid\":1,\"subCityList\":[{\"id\":4,\"name\":\"南湖\",\"pid\":3},{\"id\":5,\"name\":\"桐乡\",\"pid\":3}]}]}," +
"{\"id\":8,\"name\":\"云南\",\"pid\":0,\"subCityList\":[{\"id\":9,\"name\":\"昆明\",\"pid\":8},{\"id\":10,\"name\":\"昭通\",\"pid\":8}]}]", CityEntity.class);
} /**
* 1. 依次遍历树形List,当前节点为Cur
* a) 将Cur收集到某个存储结果的List
* b) 如果Cur有子树,压入某个栈中
* 2. 依次弹出栈元素,当前弹出的元素为StackSubTree
* a) 如果StackSubTree还有子树,继续压栈
* b) 如果StackSubTree没有子树,则放入结果List
*/
@Test
public void tree2list() {
List<CityEntity> resList = new ArrayList<>(); Stack<List<CityEntity>> stack = new Stack<>(); for (CityEntity curCity : treeList) {
resList.add(beanCopy(curCity));
if (curCity.getSubCityList() != null && !curCity.getSubCityList().isEmpty()) {
stack.push(curCity.getSubCityList());
}
} while (!stack.isEmpty()) {
List<CityEntity> subTree = stack.pop();
for (CityEntity city : subTree) {
if (city.getSubCityList() != null && !city.getSubCityList().isEmpty()) {
stack.push(city.getSubCityList());
} else {
resList.add(beanCopy(city));
}
}
} System.out.println(JSON.toJSONString(resList));
} private CityEntity beanCopy(CityEntity source) {
CityEntity res = new CityEntity();
res.setId(source.getId());
res.setName(source.getName());
res.setPid(source.getPid());
return res;
}
}

"树形List"与"扁平List"互转(Java实现)的更多相关文章

  1. .net互转java 转行必备

    .net与java其实是差不多的语言,学习起来只需要弄清楚差异及查库的方法,转起来还是很快的 以下列出几点,希望能给正在转行的你一些帮助 1,java与c#语言超详细对比 http://www.har ...

  2. 树形结构数据存储方案的选择和java list转tree

    树形结构数据存储方案 Adjacency List:每一条记录存parent_idPath Enumerations:每一条记录存整个tree path经过的node枚举Nested Sets:每一条 ...

  3. 2019 三七互娱java面试笔试题 (含面试题解析)

      本人5年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.三七互娱等公司offer,岗位是Java后端开发,因为发展原因最终选择去了三七互娱,入职一年时间了,也成为了面 ...

  4. 最新 三七互娱java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.三七互娱等10家互联网公司的校招Offer,因为某些自身原因最终选择了三七互娱.6.7月主要是做系统复习.项目复盘.Leet ...

  5. js树形数据结构的扁平化

    前面我们封装了一维数组(具备树形结构相关属性)处理成树形结构的方法:https://www.cnblogs.com/coder--wang/p/15013664.html 接下来我们来一波反向操作,封 ...

  6. 牛客网程序员面试金典:1.1确定字符互异(java实现)

    问题描述: 请实现一个算法,确定一个字符串的所有字符是否全都不同.这里我们要求不允许使用额外的存储结构. 给定一个string iniString,请返回一个bool值,True代表所有字符全都不同, ...

  7. js格式化树形数据(扁平化数据)

    需求: 1.把如下数据按照parent_id等于id的规则建立父子关系 2.同一层级的数组按照order升序 [ { "id": 1, "name": &quo ...

  8. 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的互转 JAVA

    package com.asiabasehk.cgg.util;   /**火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的互转  * Created by macremote on 1 ...

  9. JSON字符串和java对象的互转【json-lib】

    在开发过程中,经常需要和别的系统交换数据,数据交换的格式有XML.JSON等,JSON作为一个轻量级的数据格式比xml效率要高,XML需要很多的标签,这无疑占据了网络流量,JSON在这方面则做的很好, ...

  10. json字符串与java对象互转

    在开发过程中,经常需要和别的系统交换数据,数据交换的格式有XML.JSON等,JSON作为一个轻量级的数据格式比xml效率要高,XML需要很多的标签,这无疑占据了网络流量,JSON在这方面则做的很好, ...

随机推荐

  1. 引用本地的layUI

    <script src="/public/vendor/layui-v2.5.6/layui.all.js"></script>

  2. Map遍历增加key报错如何解决

    public static void main(String[] args) throws Exception{ Map<String,Object> aa=new HashMap< ...

  3. 内网jenkins跨版本升级

    概要: 原来使用的jenkins版本为1.6,现在需要升级为最新版2.3.6 由于在内网,不能使用jenkins自带的在线升级工具 升级思路: 由于版本跨度太大,直接copy jenkins目录,启动 ...

  4. vite不能用@做为路径的解决方法

    vite创建vue3后,发现原来用@做为路径的不能用了,报错信息是 Internal server error: Failed to resolve import "@ 在网上查了一下资料, ...

  5. CSS clip-path 属性

    属性定义及使用说明 clip-path 属性使用裁剪方式创建元素的可显示区域.区域内的部分显示,区域外的隐藏.可以指定一些特定形状. 注意: clip-path 属性将替换已弃用的 clip 属性. ...

  6. BitBake使用攻略--BitBake的语法知识二

    目录 写在前面 1. BitBake中的任务 2. 任务配置 2.1 依赖 2.1.1 内部任务间的依赖 2.1.2 不同菜谱下的任务间依赖 2.1.3 运行时态下的依赖 2.1.4 递归依赖 2.1 ...

  7. Java笔记第十二弹

    Lambda表达式的标准格式 三要素:形式参数.箭头.代码块 格式:(形式参数)->(代码块) 形式参数:如果有多个参数,参数之间用逗号隔开:如果没有参数,留空即可 ->代表指向动作 La ...

  8. CSP2022-S游寄

    游寄游寄,顾名思义,边游边寄 11.00AM 起床 复习了一下各种终端命令,然后又复习了一下对拍 虽然都没用到 然后接着睡. 有点小紧张,毕竟一年没搞OI 12.00AM 今天吃河虾 还行,只是有点扎 ...

  9. Facebook 的 Thrift

    更多内容,前往个人博客 Thrift 源于 Facebook,在 2007 年 Facebook 将 Thrift 作为一个开源项目提交给了 Apache 基金会.对于当时的 Facebook 来说, ...

  10. 如何提取 x64 程序那些易失的方法参数

    一:背景 1. 讲故事 最近经常遇到有朋友反馈,在 x64 环境下如何提取线程栈中的方法参数,熟悉 x64 调用协定的朋友应该知道,这种协定范围下,方法的前四个参数都是用寄存器传递的,比如rcx,rd ...