经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
"树形List"与"扁平List"互转(Java实现)
来源:cnblogs  作者:HACKYLE  时间:2023/5/12 8:52:20  对本文有异议

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

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

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

  1. id name pid
  2. 1 总公司 -1
  3. 2 上海分公司 1
  4. 3 浙江分公司 1
  5. 4 闵行区分公司 2
  6. 5 嘉兴分公司 3
  7. {
  8. "id": 1,
  9. "name": "总公司",
  10. "pid": -1,
  11. "branch":
  12. [
  13. {
  14. "id": 2,
  15. "name": "上海分公司",
  16. "pid": 1,
  17. "branch":
  18. [
  19. {
  20. "id": 4,
  21. "name": "闵行区分公司",
  22. "pid": 2,
  23. "branch":
  24. []
  25. }
  26. ]
  27. },
  28. {
  29. "id": 3,
  30. "name": "浙江分公司",
  31. "pid": 1,
  32. "branch":
  33. [
  34. {
  35. "id": 5,
  36. "name": "嘉兴分公司",
  37. "pid": 3,
  38. "branch":
  39. []
  40. }
  41. ]
  42. }
  43. ]
  44. }

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

内容导览


存储树的表结构

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

  1. DROP TABLE IF EXISTS zj_city;
  2. CREATE TABLE zj_city (
  3. id BIGINT NOT NULL AUTO_INCREMENT,
  4. name VARCHAR(50) COMMENT '节点名称',
  5. pid int NOT NULL COMMENT '父节点',
  6. create_time DATETIME DEFAULT now() COMMENT '创建时间: 年-月-日 时:分:秒',
  7. update_time DATETIME DEFAULT now() ON UPDATE now() COMMENT '更新时间',
  8. is_deleted BIT DEFAULT 0 COMMENT '是否删除:0-false-未删除;1-true-已删除',
  9. PRIMARY KEY (id)
  10. )COMMENT '浙江城市';
  11. INSERT INTO zj_city(name, pid) VALUES
  12. ('浙江省',0),
  13. ('金华市',1),
  14. ('嘉兴市',1),
  15. ('杭州市',1),
  16. ('宁波市',1);
  17. INSERT INTO zj_city(name, pid) VALUES
  18. ('下城区',4),
  19. ('钱塘区',4),
  20. ('西湖区',4),
  21. ('上城区',4);
  22. INSERT INTO zj_city(name, pid) VALUES
  23. ('南湖区',3),
  24. ('秀洲区',3),
  25. ('桐乡市',3),
  26. ('平湖市',3),
  27. ('海宁市',3);
  28. INSERT INTO zj_city(name, pid) VALUES
  29. ('梧桐街道',12),
  30. ('凤鸣街道',12),
  31. ('龙翔街道',12),
  32. ('崇福镇',12),
  33. ('乌镇镇',12),
  34. ('高桥镇',12),
  35. ('河山镇',12),
  36. ('濮院镇',12);
  37. SELECT * from zj_city;

扁平List转树形List

应用场景

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

双层for

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

  1. import com.alibaba.fastjson.JSON;
  2. import com.ks.boot.entity.CityEntity;
  3. import org.junit.jupiter.api.BeforeEach;
  4. import org.junit.jupiter.api.Test;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. public class TreeListDemo {
  8. List<CityEntity> cityEntities;
  9. /**
  10. * id name pid
  11. * 1 浙江 0
  12. * 2 杭州 1
  13. * 3 嘉兴 1
  14. * 4 南湖 3
  15. * 5 桐乡 3
  16. * 6 余杭 2
  17. * 7 西湖 2
  18. * 8 云南 0
  19. * 9 昆明 8
  20. * 10 昭通 8
  21. */
  22. @BeforeEach
  23. public void init() {
  24. cityEntities = JSON.parseArray("[{\"id\":1,\"name\":\"浙江\",\"pid\":0},\n" +
  25. "{\"id\":2,\"name\":\"杭州\",\"pid\":1},\n" +
  26. "{\"id\":3,\"name\":\"嘉兴\",\"pid\":1},\n" +
  27. "{\"id\":4,\"name\":\"南湖\",\"pid\":3},\n" +
  28. "{\"id\":5,\"name\":\"桐乡\",\"pid\":3},\n" +
  29. "{\"id\":6,\"name\":\"余杭\",\"pid\":2},\n" +
  30. "{\"id\":7,\"name\":\"西湖\",\"pid\":2},\n" +
  31. "{\"id\":8,\"name\":\"云南\",\"pid\":0},\n" +
  32. "{\"id\":9,\"name\":\"昆明\",\"pid\":8},\n" +
  33. "{\"id\":10,\"name\":\"昭通\",\"pid\":8}]", CityEntity.class);
  34. }
  35. @Test
  36. public void testList2Tree() {
  37. List<CityEntity> resultTree = list2Tree(this.cityEntities);
  38. System.out.println(JSON.toJSONString(resultTree));
  39. }
  40. /**
  41. * 双层for,List转Tree
  42. * 主要思想:外层循环-找父节点;内层循环-找子节点;因为每个元素都会找一遍,所有最终得到完整的树
  43. * 时间复杂度:N^2;空间复杂度:N
  44. */
  45. public List<CityEntity> list2Tree(List<CityEntity> cityEntities) {
  46. List<CityEntity> resultCities = new ArrayList<>();
  47. for (CityEntity city : cityEntities) {
  48. if(0 == city.getPid()) { //根节点、顶级节点,直接放入最终返回结果的List
  49. resultCities.add(city);
  50. }
  51. for (CityEntity curCity : cityEntities) { //根据当前city找它的子节点
  52. if(city.getId().equals(curCity.getPid())) {
  53. if(city.getSubCityList() == null) { //还没有任何子节点,new一个空的放进去
  54. city.setSubCityList(new ArrayList<>());
  55. }
  56. city.getSubCityList().add(curCity);
  57. }
  58. }
  59. }
  60. return resultCities;
  61. }
  62. }
  63. public class CityEntity {
  64. private Long id;
  65. private String name;
  66. private Long pid;
  67. private List<CityEntity> subCityList;
  68. getter/setter
  69. }

递归

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

  1. import com.alibaba.fastjson.JSON;
  2. import com.ks.boot.entity.CityEntity;
  3. import org.junit.jupiter.api.BeforeEach;
  4. import org.junit.jupiter.api.Test;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. public class TreeListDemo {
  8. List<CityEntity> cityEntities;
  9. /**
  10. * id name pid
  11. * 1 浙江 0
  12. * 2 杭州 1
  13. * 3 嘉兴 1
  14. * 4 南湖 3
  15. * 5 桐乡 3
  16. * 6 余杭 2
  17. * 7 西湖 2
  18. * 8 云南 0
  19. * 9 昆明 8
  20. * 10 昭通 8
  21. */
  22. @BeforeEach
  23. public void init() {
  24. cityEntities = JSON.parseArray("[{\"id\":1,\"name\":\"浙江\",\"pid\":0},\n" +
  25. "{\"id\":2,\"name\":\"杭州\",\"pid\":1},\n" +
  26. "{\"id\":3,\"name\":\"嘉兴\",\"pid\":1},\n" +
  27. "{\"id\":4,\"name\":\"南湖\",\"pid\":3},\n" +
  28. "{\"id\":5,\"name\":\"桐乡\",\"pid\":3},\n" +
  29. "{\"id\":6,\"name\":\"余杭\",\"pid\":2},\n" +
  30. "{\"id\":7,\"name\":\"西湖\",\"pid\":2},\n" +
  31. "{\"id\":8,\"name\":\"云南\",\"pid\":0},\n" +
  32. "{\"id\":9,\"name\":\"昆明\",\"pid\":8},\n" +
  33. "{\"id\":10,\"name\":\"昭通\",\"pid\":8}]", CityEntity.class);
  34. }
  35. /**
  36. * 递归,List转Tree
  37. * 主要思想:获取所有根节点、顶级节点,再从List中查找根节点的子节点;
  38. * 时间复杂度:N*(1+N)/2,O(N^2),因为递归方法中,最好是List的第一元素就是要找的子节点,最坏是
  39. * List的最后一个元素是子节点
  40. */
  41. @Test
  42. public void testList2Tree() {
  43. List<CityEntity> resultCities = new ArrayList<>();
  44. for (CityEntity city : cityEntities) {
  45. if(0 == city.getPid()) { //获取所有根节点、顶级节点,再根据根节点进行递归
  46. CityEntity topCity = findChild(cityEntities, city); //此时的topCity已经包含它的所有子节点
  47. resultCities.add(topCity);
  48. }
  49. }
  50. System.out.println(JSON.toJSONString(resultCities));
  51. }
  52. /**
  53. *
  54. * @param cityEntities 在那个里面找
  55. * @param curCity 找谁的子节点
  56. * @return curCity的子节点
  57. */
  58. public CityEntity findChild(List<CityEntity> cityEntities, CityEntity curCity) {
  59. for (CityEntity city : cityEntities) {
  60. if(curCity.getId().equals(city.getPid())) {
  61. if(null == curCity.getSubCityList()) {
  62. curCity.setSubCityList(new ArrayList<>());
  63. }
  64. CityEntity subChild = findChild(cityEntities, city); //每次递归,都从头开始查找有没有city的子节点
  65. curCity.getSubCityList().add(subChild);
  66. }
  67. }
  68. return curCity;
  69. }
  70. }
  71. public class CityEntity {
  72. private Long id;
  73. private String name;
  74. private Long pid;
  75. private List<CityEntity> subCityList;
  76. getter/setter
  77. }

转换为Map

主要思想

  • 在双层for的解法中,由于内层for也需要遍历以便List,造成时间复杂度上身为平方级
  • 如果内层for不需要遍历完整的List,而是事先预处理到Map中,寻找时直接从Map中获取,则时间复杂度降为LogN
  1. import com.alibaba.fastjson2.JSON;
  2. import org.junit.jupiter.api.BeforeEach;
  3. import org.junit.jupiter.api.Test;
  4. import java.util.ArrayList;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.stream.Collectors;
  9. public class TreeListDemo {
  10. List<CityEntity> cityEntities;
  11. /**
  12. * id name pid
  13. * 1 浙江 0
  14. * 2 杭州 1
  15. * 3 嘉兴 1
  16. * 4 南湖 3
  17. * 5 桐乡 3
  18. * 6 余杭 2
  19. * 7 西湖 2
  20. * 8 云南 0
  21. * 9 昆明 8
  22. * 10 昭通 8
  23. */
  24. @BeforeEach
  25. public void init() {
  26. cityEntities = JSON.parseArray("[{\"id\":1,\"name\":\"浙江\",\"pid\":0},\n" +
  27. "{\"id\":2,\"name\":\"杭州\",\"pid\":1},\n" +
  28. "{\"id\":3,\"name\":\"嘉兴\",\"pid\":1},\n" +
  29. "{\"id\":4,\"name\":\"南湖\",\"pid\":3},\n" +
  30. "{\"id\":5,\"name\":\"桐乡\",\"pid\":3},\n" +
  31. "{\"id\":6,\"name\":\"余杭\",\"pid\":2},\n" +
  32. "{\"id\":7,\"name\":\"西湖\",\"pid\":2},\n" +
  33. "{\"id\":8,\"name\":\"云南\",\"pid\":0},\n" +
  34. "{\"id\":9,\"name\":\"昆明\",\"pid\":8},\n" +
  35. "{\"id\":10,\"name\":\"昭通\",\"pid\":8}]", CityEntity.class);
  36. }
  37. /**
  38. * 在双层for的解法中,由于内层for也需要遍历以便List,造成时间复杂度上身为平方级
  39. * 如果内层for不需要遍历完整的List,而是事先预处理到Map中,寻找时直接从Map中获取,则时间复杂度降为LogN
  40. */
  41. @Test
  42. public void list2tree() {
  43. //收集每个PID下的节点为Map
  44. Map<Long, List<CityEntity>> cityMapByPid = cityEntities.stream().collect(Collectors.groupingBy(CityEntity::getPid));
  45. List<CityEntity> resultCityList = new ArrayList<>();
  46. for (CityEntity city : cityEntities) {
  47. if(0 == city.getPid()) { //根节点、顶点
  48. resultCityList.add(city);
  49. }
  50. List<CityEntity> citiesByPid = cityMapByPid.get(city.getId());
  51. if(null != citiesByPid && citiesByPid.size() > 0) { //有子节点
  52. if(null == city.getSubCityList()) {
  53. city.setSubCityList(new ArrayList<>());
  54. }
  55. city.getSubCityList().addAll(citiesByPid); //塞入
  56. }
  57. }
  58. System.out.println(JSON.toJSONString(resultCityList));
  59. }
  60. /**
  61. * 简化写法:在收集到Map时,对于没有子节点的节点,创建一个空的塞入到Map中
  62. */
  63. @Test
  64. public void list2tree4Simple() {
  65. List<CityEntity> resultCityList = new ArrayList<>();
  66. //保存每个PID下的子节点
  67. Map<Long, List<CityEntity>> cityMapByPid = new HashMap<>();
  68. for (CityEntity city : cityEntities) { //收集每个PID下的子节点
  69. //获取当前PID对应的子节点List,如果没有则创建一个空的List塞入
  70. //这个设计得很巧妙
  71. List<CityEntity> children = cityMapByPid.getOrDefault(city.getPid(), new ArrayList<>());
  72. children.add(city); //插入当前元素
  73. cityMapByPid.put(city.getPid(), children);
  74. }
  75. for (CityEntity city : cityEntities) {
  76. if(0 == city.getPid()) { //根节点、顶点
  77. resultCityList.add(city);
  78. }
  79. city.setSubCityList(cityMapByPid.get(city.getId()));
  80. }
  81. System.out.println(JSON.toJSONString(resultCityList));
  82. }
  83. }

主要思想

  • 收集根节点、顶级节点,存入resultList,并且压栈
  • 循环出栈,栈元素Cur
    • 找Cur的所有子元素为child
    • 如果child不为空,则再压入栈中。这一步的目的是,再一次找child的子元素
  1. import com.alibaba.fastjson2.JSON;
  2. import org.junit.jupiter.api.BeforeEach;
  3. import org.junit.jupiter.api.Test;
  4. import java.util.*;
  5. import java.util.stream.Collectors;
  6. public class TreeListDemo {
  7. List<CityEntity> cityEntities;
  8. /**
  9. * id name pid
  10. * 1 浙江 0
  11. * 2 杭州 1
  12. * 3 嘉兴 1
  13. * 4 南湖 3
  14. * 5 桐乡 3
  15. * 6 余杭 2
  16. * 7 西湖 2
  17. * 8 云南 0
  18. * 9 昆明 8
  19. * 10 昭通 8
  20. */
  21. @BeforeEach
  22. public void init() {
  23. cityEntities = JSON.parseArray("[{\"id\":1,\"name\":\"浙江\",\"pid\":0},\n" +
  24. "{\"id\":2,\"name\":\"杭州\",\"pid\":1},\n" +
  25. "{\"id\":3,\"name\":\"嘉兴\",\"pid\":1},\n" +
  26. "{\"id\":4,\"name\":\"南湖\",\"pid\":3},\n" +
  27. "{\"id\":5,\"name\":\"桐乡\",\"pid\":3},\n" +
  28. "{\"id\":6,\"name\":\"余杭\",\"pid\":2},\n" +
  29. "{\"id\":7,\"name\":\"西湖\",\"pid\":2},\n" +
  30. "{\"id\":8,\"name\":\"云南\",\"pid\":0},\n" +
  31. "{\"id\":9,\"name\":\"昆明\",\"pid\":8},\n" +
  32. "{\"id\":10,\"name\":\"昭通\",\"pid\":8}]", CityEntity.class);
  33. }
  34. /**
  35. * 主要思想:
  36. * 收集根节点、顶级节点,存入resultList,并且压栈
  37. * 循环出栈,栈元素Cur
  38. * 找Cur的所有子元素为child
  39. * 如果child不为空,则再压入栈中。这一步的目的是,再一次找child的子元素
  40. * 时间复杂度:N(过滤出所有跟节点) + 常数(出栈) * N(遍历List找当前节点的子元素)
  41. */
  42. @Test
  43. public void list2tree() {
  44. List<CityEntity> resultCityList = new ArrayList<>();
  45. Stack<CityEntity> stack = new Stack<>();
  46. resultCityList = cityEntities.stream().filter(ele -> 0 == ele.getPid()).collect(Collectors.toList());
  47. stack.addAll(resultCityList); //根节点、顶点入栈
  48. while(!stack.isEmpty()) {
  49. CityEntity curCity = stack.pop();
  50. List<CityEntity> child = cityEntities.stream().filter(ele -> curCity.getId().equals(ele.getPid())).collect(Collectors.toList());
  51. if(!child.isEmpty()) { //这一步处理的原因是:当没有子元素,不显示该个字段。流处理后没有元素只会返回空List,不会返回null
  52. curCity.setSubCityList(child);
  53. }
  54. if(!child.isEmpty()) {
  55. stack.addAll(child);
  56. }
  57. }
  58. System.out.println(JSON.toJSONString(resultCityList));
  59. }
  60. }

 

树形List转扁平List

递归

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

  1. import com.alibaba.fastjson2.JSON;
  2. import org.junit.jupiter.api.BeforeEach;
  3. import org.junit.jupiter.api.Test;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. /**
  7. * id name pid
  8. * 1 浙江 0
  9. * 2 杭州 1
  10. * 3 嘉兴 1
  11. * 4 南湖 3
  12. * 5 桐乡 3
  13. * 6 余杭 2
  14. * 7 西湖 2
  15. * 8 云南 0
  16. * 9 昆明 8
  17. * 10 昭通 8
  18. */
  19. public class ListTreeDemo {
  20. List<CityEntity> treeList;
  21. @BeforeEach
  22. public void init() {
  23. treeList = JSON.parseArray("[{\"id\":1,\"name\":\"浙江\",\"pid\":0,\"subCityList\":[" +
  24. "{\"id\":2,\"name\":\"杭州\",\"pid\":1,\"subCityList\":[{\"id\":6,\"name\":\"余杭\",\"pid\":2},{\"id\":7,\"name\":\"西湖\",\"pid\":2}]}," +
  25. "{\"id\":3,\"name\":\"嘉兴\",\"pid\":1,\"subCityList\":[{\"id\":4,\"name\":\"南湖\",\"pid\":3},{\"id\":5,\"name\":\"桐乡\",\"pid\":3}]}]}," +
  26. "{\"id\":8,\"name\":\"云南\",\"pid\":0,\"subCityList\":[{\"id\":9,\"name\":\"昆明\",\"pid\":8},{\"id\":10,\"name\":\"昭通\",\"pid\":8}]}]", CityEntity.class);
  27. }
  28. @Test
  29. public void tree2list() {
  30. List<CityEntity> resList = new ArrayList<>();
  31. //这一层for的目的是:遍历根节点
  32. for (CityEntity city : treeList) {
  33. reversion(city,resList);
  34. }
  35. System.out.println(JSON.toJSONString(resList));
  36. }
  37. public void reversion(CityEntity curNode, List<CityEntity> resList) {
  38. resList.add(beanCopy(curNode));
  39. List<CityEntity> subCityList = curNode.getSubCityList();
  40. if(subCityList != null && !subCityList.isEmpty()) {
  41. for (CityEntity city : subCityList) { //递归寻找子节点的子节点们
  42. reversion(city, resList);
  43. }
  44. }
  45. //递归的出口就是subCityList为null或者empty
  46. }
  47. private CityEntity beanCopy(CityEntity source) {
  48. CityEntity res = new CityEntity();
  49. res.setId(source.getId());
  50. res.setName(source.getName());
  51. res.setPid(source.getPid());
  52. return res;
  53. }
  54. }

主要思想

  1. 依次遍历树形List,当前节点为Cur
    1. 将Cur收集到某个存储结果的List
    2. 如果Cur有子树,压入某个栈中
  2. 依次弹出栈元素,当前弹出的元素为StackSubTree
    1. 如果StackSubTree还有子树,继续压栈
    2. 如果StackSubTree没有子树,则放入结果List
  1. import com.alibaba.fastjson2.JSON;
  2. import org.junit.jupiter.api.BeforeEach;
  3. import org.junit.jupiter.api.Test;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import java.util.Stack;
  7. /**
  8. * id name pid
  9. * 1 浙江 0
  10. * 2 杭州 1
  11. * 3 嘉兴 1
  12. * 4 南湖 3
  13. * 5 桐乡 3
  14. * 6 余杭 2
  15. * 7 西湖 2
  16. * 8 云南 0
  17. * 9 昆明 8
  18. * 10 昭通 8
  19. */
  20. public class ListTreeDemo {
  21. List<CityEntity> treeList;
  22. @BeforeEach
  23. public void init() {
  24. treeList = JSON.parseArray("[{\"id\":1,\"name\":\"浙江\",\"pid\":0,\"subCityList\":[" +
  25. "{\"id\":2,\"name\":\"杭州\",\"pid\":1,\"subCityList\":[{\"id\":6,\"name\":\"余杭\",\"pid\":2},{\"id\":7,\"name\":\"西湖\",\"pid\":2}]}," +
  26. "{\"id\":3,\"name\":\"嘉兴\",\"pid\":1,\"subCityList\":[{\"id\":4,\"name\":\"南湖\",\"pid\":3},{\"id\":5,\"name\":\"桐乡\",\"pid\":3}]}]}," +
  27. "{\"id\":8,\"name\":\"云南\",\"pid\":0,\"subCityList\":[{\"id\":9,\"name\":\"昆明\",\"pid\":8},{\"id\":10,\"name\":\"昭通\",\"pid\":8}]}]", CityEntity.class);
  28. }
  29. /**
  30. * 1. 依次遍历树形List,当前节点为Cur
  31. * a) 将Cur收集到某个存储结果的List
  32. * b) 如果Cur有子树,压入某个栈中
  33. * 2. 依次弹出栈元素,当前弹出的元素为StackSubTree
  34. * a) 如果StackSubTree还有子树,继续压栈
  35. * b) 如果StackSubTree没有子树,则放入结果List
  36. */
  37. @Test
  38. public void tree2list() {
  39. List<CityEntity> resList = new ArrayList<>();
  40. Stack<List<CityEntity>> stack = new Stack<>();
  41. for (CityEntity curCity : treeList) {
  42. resList.add(beanCopy(curCity));
  43. if (curCity.getSubCityList() != null && !curCity.getSubCityList().isEmpty()) {
  44. stack.push(curCity.getSubCityList());
  45. }
  46. }
  47. while (!stack.isEmpty()) {
  48. List<CityEntity> subTree = stack.pop();
  49. for (CityEntity city : subTree) {
  50. if (city.getSubCityList() != null && !city.getSubCityList().isEmpty()) {
  51. stack.push(city.getSubCityList());
  52. } else {
  53. resList.add(beanCopy(city));
  54. }
  55. }
  56. }
  57. System.out.println(JSON.toJSONString(resList));
  58. }
  59. private CityEntity beanCopy(CityEntity source) {
  60. CityEntity res = new CityEntity();
  61. res.setId(source.getId());
  62. res.setName(source.getName());
  63. res.setPid(source.getPid());
  64. return res;
  65. }
  66. }

 

原文链接:https://www.cnblogs.com/hackyle/p/structured-data-convert-of-tree-and-list.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号