当前位置: 首页 > news >正文

Java中List接口常见的实现类

目录

ArrayList实现类

数据存储

构造器

成员方法:CRUD

Vector实现类

数据存储

构造器方法

成员方法

LinkedList实现类

数据存储

构造器方法

成员方法CRUD

List总结


ArrayList:数组实现,随机访问速度快,增删慢,轻量级;(线程不安全)

LinkedList:双向链表实现,增删快,随机访问慢 (线程不安全)

Vector:数组实现,重量级 (线程安全、使用少)

ArrayList实现类

 List list=new ArrayList();

类定义:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, 
Cloneable, java.io.Serializable

数据存储

transient Object[] elementData; //底层存储实现采用的是数组,当存储数据元素超过数组长度时会进行扩容处理。

构造器

1、public ArrayList() {  //ArrayList默认实现中,针对元素存储的数组赋值为空数组,实际上这是一种优化策略,避免饿汉模式的缺陷,避免额外的空间浪费
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

其中常量定义为

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //空数组

2、public ArrayList(int initialCapacity) {  //参数为初始化容积,不是元素个数size。
    if (initialCapacity > 0) { 
        this.elementData = new Object[initialCapacity];  //构建指定长度的数组
    } else if (initialCapacity == 0) {  //如果初始化容积为0则为空数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {  //否则抛出异常
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

成员方法:CRUD

1、public boolean add(E e)

public boolean add(E e) {modCount++;  
//一个用于实现fail-fast异常的参数,记录的是修改次数,在迭代器中需要使用这个值,只要修改了结构则+1add(e, elementData, size); 
//参数1为需要插入的数据,参数2为正在使用的数组,参数3为当前元素个数return true;   
//除非上一步出现异常,否则返回true表示插入成功
}
private void add(E e, Object[] elementData, int s) {if (s == elementData.length)  //如果当前数组长度等于元素个数则扩容处理elementData = grow();elementData[s] = e;  //在指定位置存储元素size = s + 1;  //元素个数+1
}
private Object[] grow() {return grow(size + 1);  //新容器为元素个数+1
}
private Object[] grow(int minCapacity) {  
//参数size+1实际上是所需要的最小容积,不是扩容的目标大小int oldCapacity = elementData.length;  
//获取当前数组的长度if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {int newCapacity = ArraysSupport.newLength(oldCapacity,minCapacity - oldCapacity, oldCapacity >> 1 );  
//获取最新容积大小值,参数1为当前容积值,参数2为最小扩容值【size+1-数组长度】,参数3为老容积值的1/2整除return elementData = Arrays.copyOf(elementData, newCapacity); 
//执行老数组的元素数据拷贝,并将扩容后的新数组赋值给属性以替换老数组} else { //如果容积值<=0则创建10个长的数组用于存储元素return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];}
}

ArraySupport工具类

public static int newLength(int oldLength, int minGrowth, int prefGrowth) {int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow  
//新容器=老容积+max(最小扩容值1,老容积的1/2),扩容为+50%*原始容积if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {return prefLength;  
//扩容值在0到Integer.MAX_VALUE - 8则正常使用该扩容值} else {  
//如果扩容目标长度值大于Integer.MAX_VALUE - 8:如果原始长度+1小于0 越int界则异常中断;如果原始长度+1在Integer.MAX_VALUE - 8范围内则每次扩容到Integer.MAX_VALUE - 8;如果原始长度+1大于Integer.MAX_VALUE - 8时,则每次扩容目标值为原始长度+1return hugeLength(oldLength, minGrowth);  //原始容积 最小增长值1}
}
private static int hugeLength(int oldLength, int minGrowth) {int minLength = oldLength + minGrowth;  //原始长度+1if (minLength < 0) {  //最小长度值<0 则数据溢出,抛出异常Error中断运行throw new OutOfMemoryError("Required array length " + oldLength + " + " + minGrowth + " is too large");} else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {return SOFT_MAX_ARRAY_LENGTH;} else {return minLength;}
}

结论:添加数据时当存储数据的数组长度不足时,数组会自动变长,变长的比例为50%

2、remove方法定义

2.1 public boolean remove(Object o)  //按照元素进行删除

2.2 public E remove(int index) //按照指定下表进行删除,并返回被删除的元素

public E remove(int index) {Objects.checkIndex(index, size); 
//调用工具类Objects中的方法进行索引需要的合法性检查,如果不合法[0,size)之间,否则IndexOutOfBoundsExceptionfinal Object[] es = elementData;  
//缓存数组变量,如果需要2个不同对象但是内容相同的数组则需要clone处理,=只是将存放数据的数组地址赋值给新变量,不管使用elementData变量还是使用es变量都是在操作同一个数组E oldValue = (E) es[index];  
//从数组中获取指定index位置上的数据fastRemove(es, index);  
//采用System.arraycopy将指定位置上的数据进行覆盖return oldValue;
}
private void fastRemove(Object[] es, int i) {modCount++;  //修改次数+1final int newSize;if ((newSize = size - 1) > i)  System.arraycopy(es, i + 1, es, i, newSize - i);es[size = newSize] = null;
}

结论:使用数组元素移动的方式实现元素的删除  System.arraycopy(es, i + 1, es, i, newSize - i);

注意:这里没有变小容积

修改元素个数时会有modCount的修改--快速失败

修改次数定义在AbstractList抽象类中

protected transient int modCount = 0;
public Iterator<E> iterator() {return listIterator();  // ---new 迭代器
}

//在抽象类中采用内部类的方式提供了迭代器的实现类

private class Itr implements Iterator<E>

属性int expectedModCount = modCount;  //当创建迭代器对象时会记录当前的修改次数

public E next() {  
//调用迭代的next方法获取下一个元素时,会针对当前集合的修改次数和缓存的修改次数进行比较,如果不相等则抛出ConcurrentModificationException异常checkForComodification();//    ... ...}
final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();
}

3、get方法的实现

结论:首先要求index应该在[0,size-1]的范围内,否则异常

如果index正确则按照下标从数组中获取元素

public E get(int index) {Objects.checkIndex(index, size);return elementData(index);
}
E elementData(int index) {return (E) elementData[index];
}

Vector实现类

public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess,Cloneable, java.io.Serializable

从JDK1.0开始就提供的一个List的实现类,属于较老的不推荐使用的实现类,建议优先考虑ArrayList,因为两者的实现方式基本一致,而且ArrayList性能优于Vector.

数据存储

protected Object[] elementData;  //底层实现仍旧采用的是数组

构造器方法

1、public Vector()

public Vector() {this(10);  //无参构建Vector对象时默认初始化容积为10,默认容积增长的步长值为0
}

2、public Vector(int initialCapacity)

public Vector(int initialCapacity) {this(initialCapacity, 0);
}

3、public Vector(int initialCapacity, int capacityIncrement)

public Vector(int initialCapacity, int capacityIncrement) {super();if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);this.elementData = new Object[initialCapacity];  
//直接创建的就是指定长度的数组,并没有支持延迟创建数组的操作this.capacityIncrement = capacityIncrement;
}

成员方法

绝大部分的成员方法前有synchronized关键字,线程安全,属于重量级

1、public synchronized boolean add(E e)

public synchronized boolean add(E e) {modCount++;  //修改次数+1,快死异常add(e, elementData, elementCount);  
//参数1为需要添加的数据,参数2为原始数组,参数3为当前元素个数sizereturn true;
}
private void add(E e, Object[] elementData, int s) {if (s == elementData.length)  //当元素个数和数组长度一致时进行扩容处理growelementData = grow();elementData[s] = e;  //扩容后的数组存储元素elementCount = s + 1;  //元素个数+1
}
private Object[] grow() {return grow(elementCount + 1);   //参数为当前元素个数+1作为所需要的最小容积值
}
private Object[] grow(int minCapacity) {int oldCapacity = elementData.length;  //获取当前数组长度int newCapacity = ArraysSupport.newLength(oldCapacity,minCapacity - oldCapacity, capacityIncrement > 0 ? capacityIncrement : oldCapacity);  
//获取扩容后的新数组最佳长度值。如果设定了扩容步长值,则使用构建Vector对象时所设置的扩容步长值进行长度的递增【定值】;如果没有设置,则扩容步长值默认为0则新长度为原始长度的2倍,扩容+100%原始长度return elementData = Arrays.copyOf(elementData, newCapacity);
}

LinkedList实现类

public class LinkedList<E> extends AbstialLtractSequenist<E> implements List<E>, 
Deque<E>, Cloneable, java.io.Serializable  

实现了Deque双向队列接口

数据存储

transient Node<E> first;  //头指针
transient Node<E> last;  //尾指针
private static class Node<E> {  //静态的内部类,定义了列表中每个节点的数据类型。双向链表结构E item;  //具体存储的数据Node<E> next;  //指向下一个元素的后续指针Node<E> prev;  //指向上一个元素的前驱指针Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}
}

构造器方法

public LinkedList() {}public LinkedList(Collection<? extends E> c) {this();addAll(c);}

成员方法CRUD

1、public boolean add(E e)

public boolean add(E e) {linkLast(e);   //在链表的默认添加新元素Node类型,其中包含存储的数据ereturn true;
}
void linkLast(E e) {final Node<E> l = last; //获取尾指针final Node<E> newNode = new Node<>(l, e, null); //构建链表中的元素对象Node,其中包含数据elast = newNode;  //原始尾指针指向新元素if (l == null)  //如果尾指针为null则表示链表中没有数据,则将头和尾指向同一个元素first = newNode;else  //如果尾指针不为null,则将新元素添加到尾指针(Node元素的后续指针)之后l.next = newNode;size++;  //元素个数+1modCount++;  //修改次数+1
}

2、指定位置index序号添加元素

public void add(int index, E element) {checkPositionIndex(index);   index >= 0 && index <= sizeif (index == size)  //如果插入元素的位置等于元素个数则插入到末尾linkLast(element);else  //否则在指定位置之前插入元素linkBefore(element, node(index));  //node()用于获取指定位置的节点元素
}
Node<E> node(int index) {  
//按照索引序号查找指定位置上的元素时,首先判断序号距离头部近还是尾部近,如果距离头部近则从头指针指向的元素开始查找,如果距离尾部较劲则从后向前查找元素。可以减少1/2个数的元素查找if (index < (size >> 1)) {Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;return x;} else {Node<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;}
}

3、public boolean remove(Object o)

public boolean remove(Object o) { 
//删除定义元素。如果需要删除的元素o则从头指针开始遍历整个链表,如果Node节点对象中的数据为o【equals】则执行删除操作。删除第一个值为o的元素节点if (o == null) { for (Node<E> x = first; x != null; x = x.next) {if (x.item == null) {  unlink(x);  //删除指定的节点return true;}}} else {for (Node<E> x = first; x != null; x = x.next) {if (o.equals(x.item)) {unlink(x);return true;}}}return false;
}

4、public E remove(int index)

public E remove(int index) {  
//按照序号删除指定位置上的节点checkElementIndex(index);  
// index >= 0 && index < sizereturn unlink(node(index));  
//首先调用node()方法获取指定位置上的Node对象,然后删除指定的节点
}

总结:双向链表实现,由于没有数据移动的问题所以号称增删快【增删时还需要查找元素,所以实际上差不多O(n)】随机访问慢 (线程不安全)。没有扩容问题,按需开辟空间,没有空间浪费。

List总结

ArrayList

LinkedList

Vector

实现方式

数组,按照索引下标访问速度快O(1),但是当删除添加元素时会导致元素的移动,速度慢O(n)

双向链表,按照索引下标访问速度慢O(n),但是删除添加元素速度快O(1)

数组,按照索引下标访问速度快O(1),但是当删除添加元素时会导致元素的移动,速度慢O(n)

是否同步

不同步,线程不安全,但是并发高,访问效率高

不同步,线程不安全,但是并发高,访问效率高

同步,所以线程安全,但是并发低,访问效率低

如何选择

经常需要快速访问,较少在中间增加删除元素时使用;如果多线程访问,则需要自行编程解决线程安全问题

经常需要在内部增删元素,但是很少需要通过索引快速访问时使用;如果多线程访问,则需要自行编程解决线程安全问题

一般不使用,如果在多线程访问时可以考虑使用

相关文章:

Java中List接口常见的实现类

目录 ArrayList实现类 数据存储 构造器 成员方法&#xff1a;CRUD Vector实现类 数据存储 构造器方法 成员方法 LinkedList实现类 数据存储 构造器方法 成员方法CRUD List总结 ArrayList&#xff1a;数组实现&#xff0c;随机访问速度快&#xff0c;增删慢&#x…...

SPI通信

SPI通信&#xff1a; 四根通信线&#xff1a;SCK,MOSI,MISO,SS&#xff08;从机选择线&#xff09; 同步时钟&#xff0c;全双工 支持总线挂载多个设备&#xff0c;一主多从 SPI相对IIC传输更快&#xff0c;最简单&#xff0c;最快速 SPI没有接收和应答机制&#xff0c;发送就发…...

【动态规划】【数论】【区间合并】3041. 修改数组后最大化数组中的连续元素数目

作者推荐 视频算法专题 本文涉及知识点 动态规划汇总 数论 区间合并 LeetCode3041. 修改数组后最大化数组中的连续元素数目 给你一个下标从 0 开始只包含 正 整数的数组 nums 。 一开始&#xff0c;你可以将数组中 任意数量 元素增加 至多 1 。 修改后&#xff0c;你可以从…...

字节后端实习 一面凉经

心脏和字节永远都在跳动 深圳还有没有大厂招后端日常实习生啊&#xff0c;求捞&#xff5e;&#xff08;boss小公司也不理我&#xff09; 很纠结要不要干脆直接面暑期实习&#xff0c;又怕因为没有后端实习经历&#xff0c;面不到大厂实习。死锁了...

倒计时37天

复习1001. 马走日问题: 1.P1002 [NOIP2002 普及组] 过河卒 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) //日常碎碎念&#xff1a;谁懂啊&#xff0c;dev突然不能用了&#xff0c;也不知道是哪里出了问题下了五六次都不能用&#xff0c;&#xff0c;&#xff0c;找远程安…...

【计算机考研】考408,还是不考408性价比高?

首先综合考虑&#xff0c;如果其他科目并不是很优秀&#xff0c;需要我们花一定的时间去复习&#xff0c;408的性价比就不高&#xff0c;各个科目的时间互相挤压&#xff0c;如果备考时间不充裕&#xff0c;考虑其他专业课也未尝不可。 复习408本来就是费力不讨好的事情 不同…...

测试入门篇

测试: 这里写目录标题 测试:基础概念:BUG:创建一个合理的bug:bug 的级别:跟开发争执如何解决: 测试用例:编写测试用例的万能公式:案例: 登录功能的测试:设计测试用例的方法: 进阶篇(主要介绍测试方法):自动化测试:自动化测试的分类:selenium( web 自动化测试工具 )环境部署:什么…...

b站小土堆pytorch学习记录—— P25-P26 网络模型的使用和修改、保存和读取

文章目录 一、修改1.方法2.代码 二、保存和读取1.方法2.代码&#xff08;1&#xff09;保存&#xff08;2&#xff09;加载 3.陷阱 一、修改 1.方法 add_module(name: str, module: Module) -> None name 是要添加的子模块的名称。 module 是要添加的子模块。 调用 add_m…...

[数据结构]OJ用队列实现栈

225. 用队列实现栈 - 力扣&#xff08;LeetCode&#xff09; 官方题解&#xff1a;https://leetcode.cn/problems/implement-stack-using-queues/solutions/432204/yong-dui-lie-shi-xian-zhan-by-leetcode-solution/ 首先我们要知道 栈是一种后进先出的数据结构&#xff0c…...

「优选算法刷题」:最长回文子串

一、题目 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同&#xff0c;则该字符串称为回文字符串。 示例 1&#xff1a; 输入&#xff1a;s "babad" 输出&#xff1a;"bab" 解释&#xff1a;"aba"…...

Java项目:41 springboot大学生入学审核系统的设计与实现010

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本大学生入学审核系统管理员和学生。 管理员功能有个人中心&#xff0c;学生管理&#xff0c;学籍信息管理&#xff0c;入学办理管理等。 学生功能有…...

【数据结构与算法】常见排序算法(Sorting Algorithm)

文章目录 相关概念1. 冒泡排序&#xff08;Bubble Sort&#xff09;2. 直接插入排序&#xff08;Insertion Sort&#xff09;3. 希尔排序&#xff08;Shell Sort&#xff09;4. 直接选择排序&#xff08;Selection Sort&#xff09;5. 堆排序&#xff08;Heap Sort&#xff09;…...

Unity3D学习之XLua实践——背包系统

文章目录 1 前言2 新建工程导入必要资源2.1 AB包设置2.2 C# 脚本2.3 VSCode 的环境搭建 3 面板拼凑3.1 主面板拼凑3.2 背包面板拼凑3.3 格子复合组件拼凑3.4 常用类别名准备3.5 数据准备3.5.1 图集准备3.5.2 json3.5.3 打AB包 4 Lua读取json表及准备玩家数据5 主面板逻辑6 背包…...

前端技术研究越深入,越觉得技术不是决定录用唯一条件。

一、拒绝抬杠 我说技能不是唯一条件&#xff0c;不是说技能不重要&#xff0c;招聘前端条件是1X,其中1是技能&#xff0c;X是其他条件。 如果X条件很优秀&#xff0c;1这个条件可以降格为0.8、0.5&#xff0c;甚至更低。 有人就抬杠&#xff0c;那为啥不招聘清洁工来干前端&…...

vue组件的重新渲染的问题

目录 1.方式1 2.方式2 1.方式1 修改组件上的key属性 Vue是通过diffing算法比较虚拟DOM和真实DOM&#xff0c;来判断新旧 DOM 的变化。key是虚拟DOM对象的标识&#xff0c;在更新显示时key表示着DOM的唯一性。 DOM是否变化的核心是通过判断新旧DOM的key值是否变化&#xff0c…...

opengl 学习(二)-----你好,三角形

你好&#xff0c;三角形 分类demo效果解析 分类 opengl c demo #include "glad/glad.h" #include "glfw3.h" #include <iostream> #include <cmath> #include <vector>using namespace std;/** * 在学习此节之前&#xff0c;建议将这…...

mongodb4.2升级到5.0版本,升级到6.0版本, 升级到7.0版本案例

今天一客户想把自己当前使用的mongodb数据库4.2版本升级到7.0版本。难道mongodb能直接跳跃升级吗? 经过几经查找资料&#xff0c;貌似真不行呀。确定升级流程如下: 还得从mongo4.2升级到5.0。其次再从5.0升级到6.0。最后再从6.0升级到7.0。 开始升级之前将数据进行备份 这一步…...

CPU处理器模式与异常

ARM架构中的Exception Level&#xff08;EL&#xff09; 在ARM架构中&#xff0c;Exception Level&#xff08;EL&#xff09;是一个关键概念&#xff0c;它表示了处理器当前处理异常或中断的层次。ARMv8-A架构定义了四个Exception Levels&#xff1a;EL0、EL1、EL2和EL3&…...

Day 53 |● 1143.最长公共子序列 ● 1035.不相交的线 ● 53. 最大子序和

1143.最长公共子序列 class Solution { public:int longestCommonSubsequence(string text1, string text2) {vector<vector<int>> dp(text1.size()1,vector<int>(text2.size()1,0));int res 0;for(int i 1; i < text1.size(); i){for(int j 1; j <…...

ant-desgin charts双轴图DualAxes,柱状图无法立即显示,并且只有在调整页面大小(放大或缩小)后才开始显示

摘要 双轴图表中&#xff0c;柱状图无法立即显示&#xff0c;并且只有在调整页面大小&#xff08;放大或缩小&#xff09;后才开始显示 官方示例代码 在直接复制&#xff0c;替换为个人数据时&#xff0c;出现柱状图无法显示问题 const config {data: [data, data],xFiel…...

获取别人店铺的所有商品API接口

使用淘宝淘口令接口的步骤通常包括&#xff1a; 注册成为淘宝开放平台的开发者&#xff1a;在淘宝开放平台网站上注册账号并完成认证。 创建应用以获取API密钥&#xff1a;在您的开发者控制台中创建一个应用&#xff0c;并获取用于API调用的密钥&#xff0c;如Client ID和Clie…...

成都正信:亲戚借了钱一直不还怎么委婉的说

在中国传统文化中&#xff0c;亲情关系往往被视为最为重要和敏感的部分。当亲戚间发生借贷时&#xff0c;若出现拖欠不还的情形&#xff0c;处理起来尤为棘手。面对这样的尴尬局面&#xff0c;采取委婉而有效的沟通方式至关重要。 张华最近就遇到了这样的困扰。他的表弟去年因急…...

Truenas入门级教程

Truenas入门教程 前言&#xff1a;系统相关配置 采用I3 4160&#xff0c;采用了2块500G的硬盘&#xff0c;内存为8G&#xff0c;两个网卡只用了其中一个&#xff0c;系统安装的是core版本 硬件采用DELL3020MT机箱&#xff0c;自带3个SATA网口&#xff0c;后期网口不够&#…...

窗口函数dense() over(条件)

力扣题目连接 https://leetcode.cn/problems/rank-scores/ 在 SQL 中&#xff0c;DENSE_RANK() 是一个窗口函数&#xff0c;用于计算结果集中每行的稠密排名&#xff08;dense rank&#xff09;。DENSE_RANK() 函数会为具有相同排序字段值的行分配相同的排名&#xff0c;但不会…...

蓝牙APP开发实现汽车遥控钥匙解锁汽车智能时代

在现代社会&#xff0c;随着科技的不断发展&#xff0c;汽车已经不再是简单的交通工具&#xff0c;而是与智能科技紧密相连的载体。其中&#xff0c;通过开发APP蓝牙程序实现汽车遥控钥匙成为了一种趋势&#xff0c;为车主带来了便捷与安全的体验。虎克技术公司作为行业领先者&…...

第三天 Kubernetes进阶实践

第三天 Kubernetes进阶实践 本章介绍Kubernetes的进阶内容&#xff0c;包含Kubernetes集群调度、CNI插件、认证授权安全体系、分布式存储的对接、Helm的使用等&#xff0c;让学员可以更加深入的学习Kubernetes的核心内容。 ETCD数据的访问 kube-scheduler调度策略实践 预选与…...

redis小结

1.mysql是数据库,redis是数据库&#xff0c;那么什么时候使用应该使用哪种数据库? redis做缓存是为了缓解mysql的压力&#xff0c;在数据库表数据量上千万&#xff0c;并且访问频繁时&#xff0c;mysql压力增大&#xff0c;在有索引的情况下依旧效果不佳&#xff0c;需要使用…...

PHP伪协议详解

PHP伪协议详解 一、前言1.什么是PHP伪协议&#xff1f;2.什么时候用PHP伪协议? 二、常见的php伪协议php://inputphp://filterzip://与bzip2://与zlib://协议data://phar:// 一、前言 1.什么是PHP伪协议&#xff1f; PHP伪协议是PHP自己支持的一种协议与封装协议&#xff0c;…...

进程:守护进程

一、守护进程的概念 守护进程是脱离于终端控制&#xff0c;且运行在后端的进程。&#xff08;孤儿进程&#xff09;守护进程不会将信息显示在任何终端上影响前端的操作&#xff0c;也不会被终端产生的任何信息打断&#xff0c;例如&#xff08;ctrlc&#xff09;.守护进程独立…...

千里马平台项目管理理念

软件项目的成功和失败和技术关系不大&#xff0c;尤其是应用型软件&#xff0c;不可能有技术难关卡死了项目&#xff0c;大部分问题还是出现在项目管理层面。公司的业务形态是帮助客户构建自己的信息化生态圈&#xff0c;项目管理咨询也是其中的核心内容。 我们的软件项目管理理…...