Java集合之ArrayList与LinkList
注:示例基于JDK1.8版本
参考资料:Java知音公众号
本文超长,也是搬运的干货,希望小伙伴耐心看完。
Collection集合体系
List、Set、Map是集合体系的三个接口。
其中List和Set继承了Collection接口。
List有序且元素可以重复,默认大小为10;ArrayList、LinkedList和Vector是三个主要的实现类。
Set元素不可以重复,HashSet和TreeSet是两个主要的实现类。
Map也属于集合系统,但和Collection接口不同。Map是key-value键值对形式的集合,key值不能重复,value可以重复;HashMap、TreeMap和Hashtable是三个主要的实现类。
--------------------------------------------------------------------------------------------------------------------
一、ArrayList
ArrayList基层是以数组实现的,可以存储任何类型的数据,但数据容量有限制,超出限制时会扩增50%容量,查找元素效率高。
ArrayList是一个简单的数据结构,因超出容量会自动扩容,可认为它是常说的动态数组。
源码分析
A、属性分析
/**
* 默认初始化容量
*/
private static final int DEFAULT_CAPACITY = 10; /**
* 如果自定义容量为0,则会默认用它来初始化ArrayList。或者用于空数组替换。
*/
private static final Object[] EMPTY_ELEMENTDATA = {}; /**
* 如果没有自定义容量,则会使用它来初始化ArrayList。或者用于空数组比对。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /**
* 这就是ArrayList底层用到的数组 * 非私有,以简化嵌套类访问
* transient 在已经实现序列化的类中,不允许某变量序列化
*/
transient Object[] elementData; /**
* 实际ArrayList集合大小
*/
private int size; /**
* 可分配的最大容量
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
B、构造方法分析
1、不带参数初始化,默认容量为10
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
2、根据initialCapacity初始化一个空数组,如果值为0,则初始化一个空数组
/**
* 根据initialCapacity 初始化一个空数组
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
3、通过集合做参数的形式初始化,如果集合为空,则初始化为空数组
/**
* 通过集合做参数的形式初始化
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
C、主要方法
1、trimToSize()方法:
用来最小实例化存储,将容器大小调整为当前元素所占用的容量大小。
/**
* 这个方法用来最小化实例存储。
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
判断size的值,若为0,则将elementData置为空集合,若大于0,则将一份数组容量大小的集合复制给elementData。
2、clone()方法
克隆一个新数组。
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
通过调用Object
的clone()
方法来得到一个新的ArrayList
对象,然后将elementData
复制给该对象并返回。
3、add(E e)
在ArrayList末尾添加元素。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
该方法首先调用了ensureCapacityInternal()方法,注意参数是size+1(数组已有参数个数+1个新参数),先来看看ensureCapacityInternal的源码:
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
方法说明:计算容量+确保容量(上上代码)
计算容量:若elementData为空,则minCapacity值为默认容量和size+1(minCapacity)的最大值;若elementData不为空,minCapacity(size+1)不用进行操作
确保容量:如果size+1 > elementData.length
证明数组已经放满,则增加容量,调用grow()
。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//计算修改次数 // overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
} private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; // 扩展为原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1); // 如果扩为1.5倍还不满足需求,直接扩为需求值
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
增加容量grow():默认1.5倍扩容。
获取当前数组长度=>oldCapacity
oldCapacity>>1 表示将oldCapacity右移一位(位运算),相当于除2。再加上1,相当于新容量扩容1.5倍。
如果
newCapacity<mincapacity`,则`newcapacity mincapacity="size+1=2" elementdata="1" newcapacity="1+1""">>1=1
,1<2
所以如果不处理该情况,扩容将不能正确完成。如果新容量比最大值还要大,则将新容量赋值为VM要求最大值。
将elementData拷贝到一个新的容量中。
也就是说,当增加数据的时候,如果ArrayList的大小已经不满足需求时,那么就将数组变为原长度的1.5倍,之后的操作就是把老的数组拷到新的数组里面。
例如,默认的数组大小是10,也就是说当我们add10个元素之后,再进行一次add时,就会发生自动扩容,数组长度由10变为了15具体情况如下所示:
4、add(int index, E element)方法
public void add(int index, E element) {
rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
rangeCheckForAdd()是越界异常检测方法。
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
System.arraycopy方法:
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
Object src : 原数组
int srcPos : 从元数据的起始位置开始
Object dest : 目标数组
int destPos : 目标数组的开始起始位置
int length : 要copy的数组的长度
5、set(int index, E element)方法:
用指定元素替换此列表中指定位置的元素。
public E set(int index, E element) {
rangeCheck(index); E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
} E elementData(int index) {
return (E) elementData[index];
}
6、indexOf(Object o):
返回数组中第一个与参数相等的值的索引,允许null。
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
7、get(int index)方法:
返回指定下标处的元素的值。
public E get(int index) {
rangeCheck(index); return elementData(index);
}
rangeCheck(index)
会检测index值是否合法,如果合法则返回索引对应的值。
8、remove(int index):
删除指定下标的元素。
public E remove(int index) {
// 检测index是否合法
rangeCheck(index);
// 数据结构修改次数
modCount++;
E oldValue = elementData(index); // 记住这个算法
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work return oldValue;
}
ArrayList优缺点
优点:
1、因为其底层是数组,所以修改和查询效率高。
2、自动扩容(1.5倍)。
缺点:
1、插入和删除效率不高。(文末对比LinkedList)
2、线程不安全。
二、LinkedList
LinkList以双向链表实现,链表无容量限制,但双向链表本身使用了更多空间,每插入一个元素都要构造一个额外的Node对象,也需要额外的链表指针操作。允许元素为null,线程不安全。
源码分析
1、变量
/**
* 集合元素数量
**/
transient int size = 0; /**
* 指向第一个节点的指针
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first; /**
* 指向最后一个节点的指针
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
2、构造方法
/**
* 无参构造方法
*/
public LinkedList() {
} /**
* 将集合c所有元素插入链表中
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
3、Node节点
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;
}
}
Node既有prev也有next,所以证明它是一个双向链表。
4、添加元素
a、addAll(Collection c)方法
将集合c添加到链表,如果不传index,则默认是添加到尾部。如果调用addAll(int index, Collection<? extends E> c)
方法,则添加到index后面。
/**
* 将集合添加到链尾
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
} /**
*
*/
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index); // 拿到目标集合数组
Object[] a = c.toArray();
//新增元素的数量
int numNew = a.length;
//如果新增元素数量为0,则不增加,并返回false
if (numNew == 0)
return false; //定义index节点的前置节点,后置节点
Node<E> pred, succ;
// 判断是否是链表尾部,如果是:在链表尾部追加数据
//尾部的后置节点一定是null,前置节点是队尾
if (index == size) {
succ = null;
pred = last;
} else {
// 如果不在链表末端(而在中间部位)
// 取出index节点,并作为后继节点
succ = node(index);
// index节点的前节点 作为前驱节点
pred = succ.prev;
} // 链表批量增加,是靠for循环遍历原数组,依次执行插入节点操作
for (Object o : a) {
@SuppressWarnings("unchecked")
// 类型转换
E e = (E) o;
// 前置节点为pred,后置节点为null,当前节点值为e的节点newNode
Node<E> newNode = new Node<>(pred, e, null);
// 如果前置节点为空, 则newNode为头节点,否则为pred的next节点
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
} // 循环结束后,如果后置节点是null,说明此时是在队尾追加的
if (succ == null) {
// 设置尾节点
last = pred;
} else {
//否则是在队中插入的节点 ,更新前置节点 后置节点
pred.next = succ;
succ.prev = pred;
} // 修改数量size
size += numNew;
//修改modCount
modCount++;
return true;
} /**
* 取出index节点
*/
Node<E> node(int index) {
// assert isElementIndex(index); // 如果index 小于 size/2,则从头部开始找
if (index < (size >> 1)) {
// 把头节点赋值给x
Node<E> x = first;
for (int i = 0; i < index; i++)
// x=x的下一个节点
x = x.next;
return x;
} else {
// 如果index 大与等于 size/2,则从后面开始找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
} // 检测index位置是否合法
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
} // 检测index位置是否合法
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
假设我们要在index=2处添加{1,2}到链表中,图解如下:
第一步:拿到index=2的前驱节点 prev=ele1
第二步:遍历集合prev.next=newNode,并实时更新prev节点以便下一次遍历:prev=newNode
第三步:将index=2的节点ele2接上:prev.next=ele2,ele2.prev=prev
注意node(index)方法:方法:寻找处于index的节点,有一个小优化,结点在前半段则从头开始遍历,在后半段则从尾开始遍历,这样就保证了只需要遍历最多一半结点就可以找到指定索引的结点。
b、addFirst(E e)方法
将e元素添加到链表并设置其为头节点(first)。
public void addFirst(E e) {
linkFirst(e);
} //将e链接成列表的第一个元素
private void linkFirst(E e) { final Node<E> f = first;
// 前驱为空,值为e,后继为f
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
//若f为空,则表明列表中还没有元素,last也应该指向newNode
if (f == null)
last = newNode;
else
//否则,前first的前驱指向newNode
f.prev = newNode;
size++;
modCount++;
}
拿到first节点命名为f
新创建一个节点newNode设置其next节点为f节点
将newNode赋值给first
若f为空,则表明列表中还没有元素,last也应该指向newNode;否则,前first的前驱指向newNode。
图解如下:
c、addLast(E e)方法
将e元素添加到链表并设置其为尾节点(last)。
public void addLast(E e) {
linkLast(e);
}
/**
* 将e链接成列表的last元素
*/
void linkLast(E e) {
final Node<E> l = last;
// 前驱为前last,值为e,后继为null
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
//最后一个节点为空,说明列表中无元素
if (l == null)
//first同样指向此节点
first = newNode;
else
//否则,前last的后继指向当前节点
l.next = newNode;
size++;
modCount++;
}
过程与linkFirst()
方法类似,这里略过。
d、add(E e)方法
在尾部追加元素e。
public boolean add(E e) {
linkLast(e);
return true;
} void linkLast(E e) {
final Node<E> l = last;
// 前驱为前last,值为e,后继为null
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
//最后一个节点为空,说明列表中无元素
if (l == null)
//first同样指向此节点
first = newNode;
else
//否则,前last的后继指向当前节点
l.next = newNode;
size++;
modCount++;
}
e、add(int index, E element)方法
在链表的index处添加元素element.
public void add(int index, E element) {
checkPositionIndex(index); if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
/**
* 在succ节点前增加元素e(succ不能为空)
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
// 拿到succ的前驱
final Node<E> pred = succ.prev;
// 新new节点:前驱为pred,值为e,后继为succ
final Node<E> newNode = new Node<>(pred, e, succ);
// 将succ的前驱指向当前节点
succ.prev = newNode;
// pred为空,说明此时succ为首节点
if (pred == null)
// 指向当前节点
first = newNode;
else
// 否则,将succ之前的前驱的后继指向当前节点
pred.next = newNode;
size++;
modCount++;
}
5、获取/查询元素
a、get(int index)
根据索引获取链表中的元素。
public E get(int index) {
checkElementIndex(index);
return node(index).item;
} // 检测index合法性
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
} // 根据index 获取元素
Node<E> node(int index) {
// assert isElementIndex(index); 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;
}
}
b、getFirst()方法
获取头节点。
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
c、getLast()方法
获取尾节点。
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
6、删除元素
a、remove(Object o)
根据Object对象删除元素。
public boolean remove(Object o) {
// 如果o是空
if (o == null) {
// 遍历链表查找 item==null 并执行unlink(x)方法删除
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;
} E unlink(Node<E> x) {
// assert x != null;
// 保存x的元素值
final E element = x.item;
//保存x的后继
final Node<E> next = x.next;
//保存x的前驱
final Node<E> prev = x.prev; //如果前驱为null,说明x为首节点,first指向x的后继
if (prev == null) {
first = next;
} else {
//x的前驱的后继指向x的后继,即略过了x
prev.next = next;
// x.prev已无用处,置空引用
x.prev = null;
} // 后继为null,说明x为尾节点
if (next == null) {
// last指向x的前驱
last = prev;
} else {
// x的后继的前驱指向x的前驱,即略过了x
next.prev = prev;
// x.next已无用处,置空引用
x.next = null;
}
// 引用置空
x.item = null;
size--;
modCount++;
// 返回所删除的节点的元素值
return element;
}
遍历链表查找 item==null 并执行unlink(x)方法删除
如果前驱为null,说明x为首节点,first指向x的后继,x的前驱的后继指向x的后继,即略过了x.
如果后继为null,说明x为尾节点,last指向x的前驱;否则x的后继的前驱指向x的前驱,即略过了x,置空x.next
引用置空:
x.item = null
图解:
b、remove(int index)方法
根据链表的索引删除元素。
public E remove(int index) {
checkElementIndex(index);
//node(index)会返回index对应的元素
return unlink(node(index));
}
c、removeLast()方法
删除尾节点。
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
} private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
// 取出尾节点中的元素
final E element = l.item;
// 取出尾节点中的后继
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
// last指向前last的前驱,也就是列表中的倒数2号位
last = prev;
// 如果此时倒数2号位为空,那么列表中已无节点
if (prev == null)
// first指向null
first = null;
else
// 尾节点无后继
prev.next = null;
size--;
modCount++;
// 返回尾节点保存的元素值
return element;
}
7、修改元素
修改元素比较简单,先找到index对应节点,然后对值进行修改。
public E set(int index, E element) {
checkElementIndex(index);
// 获取到需要修改元素的节点
Node<E> x = node(index);
// 保存之前的值
E oldVal = x.item;
// 执行修改
x.item = element;
// 返回旧值
return oldVal;
}
LinkedList优点:不需要扩容和预留空间,空间效率高。
三、ArrayList与LinkedList插入和查找消耗时间测试对比
参考链接:https://blog.csdn.net/dearKundy/article/details/84663512
在ArrayList和LinkedList的头部、尾部和中间三个位置插入与查找100000个元素所消耗的时间来进行对比测试,下面是测试结果:(时间单位ms)
插入 | 查找 | |
ArrayList尾部 | 26 | 12 |
ArrayList头部 | 859 | 7 |
ArrayList中间 | 1848 | 13 |
LinkedList尾部 | 28 | 9 |
LinkedList头部 | 15 | 11 |
LinkedList中间 | 15981 | 34928 |
测试结论:
ArrayList的查找性能绝对是一流的,无论查询的是哪个位置的元素
ArrayList除了尾部插入的性能较好外(位置越靠后性能越好),其他位置性能就不如人意了
LinkedList在头尾查找、插入性能都是很棒的,但是在中间位置进行操作的话,性能就差很远了,而且跟ArrayList完全不是一个量级的
根据源码分析所得结论:
- 对于LinkedList来说,头部插入和尾部插入时间复杂度都是O(1)
- 但是对于ArrayList来说,头部的每一次插入都需要移动size-1个元素,效率可想而知
- 但是如果都是在最中间的位置插入的话,ArrayList速度比LinkedList的速度快将近10倍
Java集合之ArrayList与LinkList的更多相关文章
- 从源码看Java集合之ArrayList
Java集合之ArrayList - 吃透增删查改 从源码看初始化以及增删查改,学习ArrayList. 先来看下ArrayList定义的几个属性: private static final int ...
- Java:List,ArrayList和LinkList的区别
1.大学数据结构中ArrayList是实现了基于动态数组的数据结构,LinkList基于链表的数据结构 2.对于随机访问get和set,ArrayList优于LinkList,因为LinkedList ...
- Java集合关于ArrayList
ArrayList实现源码分析 2016-04-11 17:52 by 淮左, 207 阅读, 0 评论, 收藏, 编辑 本文将以以下几个问题来探讨ArrayList的源码实现1.ArrayList的 ...
- Java集合干货——ArrayList源码分析
ArrayList源码分析 前言 在之前的文章中我们提到过ArrayList,ArrayList可以说是每一个学java的人使用最多最熟练的集合了,但是知其然不知其所以然.关于ArrayList的具体 ...
- java集合之ArrayList,TreeSet和HashMap分析
java集合是一个重点和难点,如果我们刻意记住所有的用法与区别则是不太现实的,之前一直在使用相关的集合类,但是没有仔细研究区别,现在来把平时使用比较频繁的一些集合做一下分析和总结,目的就是以后在需要使 ...
- Java集合:ArrayList的实现原理
Java集合---ArrayList的实现原理 目录: 一. ArrayList概述 二. ArrayList的实现 1) 私有属性 2) 构造方法 3) 元素存储 4) 元素读取 5) 元素删除 ...
- 【源码阅读】Java集合之一 - ArrayList源码深度解读
Java 源码阅读的第一步是Collection框架源码,这也是面试基础中的基础: 针对Collection的源码阅读写一个系列的文章,从ArrayList开始第一篇. ---@pdai JDK版本 ...
- Java集合(六)--ArrayList、LinkedList和Vector对比
在前两篇博客,学习了ArrayList和LinkedList的源码,地址在这: Java集合(五)--LinkedList源码解读 Java集合(四)--基于JDK1.8的ArrayList源码解读 ...
- Java 集合源代码——ArrayList
(1)可以查看大佬们的 详细源码解析 : 连接地址为 : https://blog.csdn.net/zhumingyuan111/article/details/78884746 (2) Array ...
随机推荐
- UVA 1635 Irrelevant Elements
https://vjudge.net/problem/UVA-1635 题意:n个数,每相邻两个求和,最后变成1个数,问这个数除m的余数与第几个数无关 n个数使用次数分别为C(n-1,i) i∈[0, ...
- K-近邻(KNN)算法
1,KNN算法对未知类别属性的数据集中的每个点依次执行以下操作: 计算已知类别数据集中的点与当前点之间的距离; 按照距离递增排序; 选取与当前点距离最小的k个点; 确定前k个点所在类别的出现频率; 返 ...
- Item 12 考虑实现Comparable接口
1.Comparable接口,用来做什么. 2.判定类实现的Comparable接口是否正确的方法. 3.不要扩展一个已经实现了Comparable接口的类来增加用于比较的值组件. 1.Com ...
- 【洛谷 P3899】 [湖南集训]谈笑风生 (主席树)
题目链接 容易发现\(a,b,c\)肯定是在一条直链上的. 定义\(size(u)\)表示以\(u\)为根的子树大小(不包括\(u\)) 分两种情况, 1.\(b\)是\(a\)的祖先,对答案的贡献是 ...
- .net WebAPI返回xml、json格式
WebAPI返回xml.json格式简单示例 using System.Net.Http.Formatting; public class TestController : ApiController ...
- vs调试 配置IISExpress允许局域网内部访问
内网可访问后,本机不能使用localhost 1.找到IISExpress的配置文件,位于 <文档>/IISExpress/config文件夹下,打开applicationhost.c ...
- Sketch VS Photoshop
参考:http://mp.weixin.qq.com/s?__biz=MjM5NTQ5MjIyMA==&mid=217309554&idx=4&sn=4d6a5239ca813 ...
- linux下删除已经不用的配置文件
使用命令 dpkg -l | grep -v ^ii 查看当前未安装或者不用了的配置文件 例如我的显示如下
- 手動設定 電池溫度 mtk platform
adb root adb shell echo "3 1 27" > ./proc/mtk_battery_cmd/battery_cmd 27 即是所要設定的溫度, 此設定 ...
- 工具安装===Sublime Text-安装
Sublime Text 是一款通用型轻量级编辑器,支持多种编程语言.有许多功能强大的快捷键(如 Ctrl+d),支持丰富的插件扩展.如果平时需要在不同编程语言间切换,那么它将会是一个,不错的选择. ...