关于arraylist:ArrayList-的工作原理

ArrayList 的工作原理ArrayList has 是一个外部的一般数组,它充当数据存储。在大多数情况下,咱们不指定列表的确切大小。然而外部数组必须有一些大小!它的确如此。 它的默认大小是 10 。 public static void main(String[] args) { ArrayList<Car> cars = new ArrayList<>();}复制代码首先,让咱们看看增加新元素是什么样的。首要任务是查看 外部数组在外部数组中是否有足够的空间, 以及是否可能再放一个元素。如果有空间,则将新元素增加到列表的开端。当咱们说“到最初”时,咱们并不是指数组中的最初一个地位(那会很奇怪)。咱们指的是最初一个以后元素之后的地位。它的索引将是 cars.size() 。咱们的列表目前是空的 ( cars.size() == 0 )。因此,新元素将被增加到地位 0。 ArrayList<Car> cars = new ArrayList<>();Car ferrari = new Car("Ferrari 360 Spider");cars.add(ferrari);复制代码这很明显。如果咱们在两头插入会发生什么,即在其余元素之间? public static void main(String[] args) { ArrayList<Car> cars = new ArrayList<>(); Car ferrari = new Car("Ferrari 360 Spider"); Car bugatti = new Car("Bugatti Veyron"); Car lambo = new Car("Lamborghini Diablo"); Car ford = new Car("Ford Modneo"); cars.add(ferrari); cars.add(bugatti); cars.add(lambo); cars.add(1, ford);// add ford to cell 1, which is already occupied}复制代码同样,首先查看数组中是否有足够的空间。如果有足够的空间,则 元素向右移动 ,从咱们插入新元素的地位开始。咱们在地位 1 插入。换句话说,从地位 3 的元素被复制到地位 4,元素 2 到地位 3,元素 1 到地位 2。 ...

June 9, 2022 · 1 min · jiezi

关于arraylist:数据结构和算法-ArrayList

1. 什么是线性表对于线性表的概念以及LinkedList和ArrayList的区别能够参考上篇文章。 Segment Fault Bugkit 上面咱们间接看看如何用Java实现ArrayList 2. java实现其中的抽象类AbstractList和接口List也是本人定义的,如须要看残缺代码,能够到文末的Github查看残缺代码。/** * @param <E> Type of element * @author bugkit * @since 2021.10.27 */public class ArrayList<E> extends AbstractList<E> implements List<E> { // 存储元素的数组 private E[] data; // 元素的默认容量,最多只能装8个 private int capacity = 8; // 创立容量为8的数组 public ArrayList() { data = (E[]) new Object[capacity]; } // 创立容量为传入参数大小的数组 public ArrayList(int capacity) { this.capacity = capacity; data = (E[]) new Object[capacity]; } // 获取在data数组中的第i个元素 @Override public E get(int i) { if (i >= size) { throw new IndexOutOfBoundsException("Index " + i + " is out of range " + size); } return data[i]; } // 移除在data数组中的第i个元素 @Override public E remove(int i) { if (i >= size) { throw new IndexOutOfBoundsException("Index " + i + " is out of range " + size); } E e = data[i]; System.arraycopy(data, i + 1, data, i, size - i); size--; return e; } // 在data数组的第i个地位插入新的元素,如果i大于以后元素的个数则会报错数组越界 @Override public boolean add(E e, int i) { if (i > size) { throw new IndexOutOfBoundsException("Index " + i + " is out of range " + size); } if (size == capacity - 1) { resize(); } if (size + 1 - i >= 0) System.arraycopy(data, i, data, i + 1, size + 1 - i); data[i] = e; size++; return true; } // 增加元素到开端 @Override public boolean add(E e) { return add(e, size); } // 移除特定元素,如果元素存在则移除该元素并返回true,否则返回false @Override public boolean remove(E e) { for (int i = 0; i < size; i++) { if (data[i].equals(e)) { remove(i); return true; } } return false; } // 判断ArrayList是否存在元素e @Override public boolean contains(E e) { for (int i = 0; i < size; i++) { if (data[i].equals(e)) { return true; } } return false; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("ArrayList { "); for (int i = 0; i < size; i++) { sb.append(data[i]).append(", "); } sb.replace(sb.length() - 1, sb.length(),"").append(" }"); return sb.toString(); } // 扩容,当add元素后,size == capacity,则会将ArrayList扩容为原来的两倍, // 并将原来的元素按程序拷贝到新的data数组 private void resize() { capacity = capacity * 2; E[] newData = (E[]) new Object[capacity]; System.arraycopy(data, 0, newData, 0, size); data = newData; }}

October 29, 2021 · 2 min · jiezi

关于arraylist:集合篇ArrayList源码解析

和 LinkedList的区别:ArrayList: 底层是动静数组,反对扩容,线程不平安。对于增加和删除办法,如果是增加到列表尾部,工夫复杂度是O(1);如果是增加到指定地位i,工夫复杂度就是O(n-i),因为须要将后续数组的元素往后挪动一位;对于疾速随机拜访get(i),工夫复杂度是O(1)。LinkedList: 双向链表构造,线程不平安。对于增加和删除办法,如果是增加到列表尾部,因为是间接操作最初一个last节点,所以是O(1);如果是增加到指定地位i,因为须要先挪动到链表的指定地位,所以工夫复杂度是O(i);不反对疾速随机拜访,工夫复杂度O(n)。所以一般来说,因为反对疾速随机拜访,所以在查问元素方面,ArrayList有劣势,相同的,在增加和删除元素方面,LinkedList不须要挪动数据,所以更有劣势。当然这不是相对的,因为LinkedList也须要先遍历到指定地位(开端节点除外),具体应用还是须要依据理论业务场景来综合考量。 外围源码解析public class ArrayList<E> { private static final long serialVersionUID = 8683452581122892189L; /** * 默认初始容量 */ private static final int DEFAULT_CAPACITY = 10; /** * 用于指定大小为0的空实例时的数组实例. */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 用于默认大小的空实例的共享空数组实例。 * 将它与 EMPTY_ELEMENTDATA 辨别开来,以理解增加第一个元素时要收缩多少 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * 存储ArrayList元素的数组缓冲区。 * * ArrayList 的容量就是这个数组缓冲区的长度。任何带有 * elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的 * 空 ArrayList增加第一个元素时将扩大为 DEFAULT_CAPACITY 大小。 */ transient Object[] elementData; /** * ArrayList 的大小(它蕴含的元素数) */ private int size; /** * 记录List对象被批改的次数,List对象每批改一次,modCount都会加1. */ protected transient int modCount = 0; /** * 有参构造函数,结构一个具备指定初始容量的数组. */ 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); } } /** * 无参构造函数,结构一个初始容量为 10 的空数组(当增加第一个元素时才为10) * * 有参大小为0和无参的构造函数应用的空数组不一样,前者是EMPTY_ELEMENTDATA,后者是DEFAULTCAPACITY_EMPTY_ELEMENTDATA */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * 依照汇合的迭代器返回的程序结构一个蕴含指定汇合元素的数组 */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { //这里,例如当应用Arrays.asList来构建c时,c.toArray().getClass()的后果就不是Object[].class了,而是String[].class //如果是这种状况,就须要将非Object数组类型的elementData转成Object数据类型 if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { this.elementData = EMPTY_ELEMENTDATA; } } /** * 将ArrayList实例的容量修剪为列表的以后大小。 * 应用程序能够应用此操作来最小化实例的存储 */ public void trimToSize() { modCount++; //size是数组中理论元素的个数,而elementData.length代表的是数组的大小 if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); //顺便提一下,Arrays.copyOf办法是创立一个大小为size的新数组,依照size和elementData.length中更小的单位复制elementData中的元素。 //当然,如果传入的size大于elementData.length话,也就相当于扩容,反之则是裁剪 } } /** * 扩容机制 * * 减少实例的容量,如果必要,以确保它至多能够包容元素的数量 * 由最小容量参数指定 * @param minCapacity 所需最小容量 */ public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // 这里要阐明一下,只有在创立不指定大小的空实例时,扩容才会用到默认初始容量DEFAULT_CAPACITY ? 0 : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } /** * 先判断数组缓冲区是不是没有增加过元素,如果是,则返回设置容量和默认初始容量的最大值 * @param elementData * @param minCapacity * @return */ private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // 如果要扩容的数组大小大于缓冲数组的大小,才能够执行扩容,否则不会扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); } /** * 要调配的数组的最大大小 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** * 扩容机制具体实现 */ private void grow(int minCapacity) { int oldCapacity = elementData.length; // >> 1 意思是获取旧数组的一半长度,扩容相当于将数组长度扩大成原来的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); //如果还是小于最小容量,则间接把最小容量作为新容量 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 依照新容量扩容 elementData = Arrays.copyOf(elementData, newCapacity); } /** * 比拟 minCapacity 和 MAX_ARRAY_SIZE,如果 minCapacity 大于最大容量,则新容量则为Integer.MAX_VALUE, * 否则,新容量大小则为 MAX_ARRAY_SIZE 即为 Integer.MAX_VALUE - 8 */ private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } /** * 返回此列表中的元素数 * */ public int size() { return size; } /** * 如果此列表不蕴含元素,则返回 true * */ public boolean isEmpty() { return size == 0; } /** * 如果此列表蕴含指定的元素,则返回true */ public boolean contains(Object o) { return indexOf(o) >= 0; } /** * 返回指定元素第一次呈现的索引,如果此列表不蕴含该元素,则为 -1 */ 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; } /** * 返回指定元素最初一次呈现的索引,如果此列表不蕴含该元素,则为 -1 */ public int lastIndexOf(Object o) { if (o == null) { for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } return -1; } /** * 返回此实例的浅拷贝。 (元素自身不会被复制。) */ 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); } } /** * 以适当的程序(从第一个元素到最初一个元素)返回一个蕴含此列表中所有元素的数组 * 返回的数组将是“平安的”,因为列表实例没有对它的援用。(换句话说,这个办法调配了 * 一个新数组),因而调用者能够自在地批改返回的数组。 */ public Object[] toArray() { return Arrays.copyOf(elementData, size); } /** * 以适当的形式返回蕴含此列表中所有元素的数组序列(从第一个元素到最初一个元素); * 返回的运行时类型数组是指定数组的数组; * 如果列表合乎指定的数组,它在其中返回; * 否则,应用指定数组的运行时类型和大小调配一个新数组。 * * 如果列表适宜指定的数组并有残余空间(即,数组的元素比列表多), * 紧跟在汇合开端之后的数组被设置为空。 * (这在确定长度时很有用,如果调用者晓得列表不蕴含任何空元素。) */ @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { if (a.length < size) // Make a new array of a's runtime type, but my contents: return (T[]) Arrays.copyOf(elementData, size, a.getClass()); System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } @SuppressWarnings("unchecked") E elementData(int index) { return (E) elementData[index]; } /** * 返回此列表中指定地位的元素 */ public E get(int index) { rangeCheck(index); return elementData(index); } /** * 将此列表中指定地位的元素替换为指定的元素。 */ public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; } /** * 将指定的元素附加到此列表的开端。 */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } /** * 在此指定地位插入指定元素。 * 挪动以后在该地位的元素(如果有)和左边的任何后续元素(在它们的索引上加一)。 */ 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++; } /** * 移除此列表中指定地位的元素。 * 将任何后续元素向左挪动(从它们的元素中减去一个) */ public E remove(int 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; } /** * 从此列表中删除第一次呈现的指定元素(如果它存在); * 如果列表不蕴含该元素,则为不变。 */ public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } /** * 跳过边界查看且不执行的公有删除办法 */ private void fastRemove(int index) { modCount++; 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 } /** * 从此列表中删除所有元素。 * 该列表在调用返回后将会为空。 */ public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; } /** * 将指定汇合中的所有元素追加到开端(依照它们指定汇合的迭代器返回的程序)。 * */ public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; } /** * 将指定汇合中的所有元素插入到此列表,从指定地位开始。 * */ public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; } /** * 从此列表中删除索引介于两者之间的所有元素 * */ protected void removeRange(int fromIndex, int toIndex) { modCount++; int numMoved = size - toIndex; System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); // clear to let GC do its work int newSize = size - (toIndex-fromIndex); for (int i = newSize; i < size; i++) { elementData[i] = null; } size = newSize; } /** * 查看给定的索引是否在范畴内 */ private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } /** * add 和 addAll 应用的 rangeCheck 版本. */ private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } /** * 结构一个 IndexOutOfBoundsException 具体音讯 */ private String outOfBoundsMsg(int index) { return "Index: "+index+", Size: "+size; } /** * 从此列表中删除指定汇合的所有元素 * */ public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, false); } /** * 仅保留此列表中蕴含在指定汇合中的元素. * */ public boolean retainAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, true); } private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; int r = 0, w = 0; boolean modified = false; try { for (; r < size; r++) if (c.contains(elementData[r]) == complement) elementData[w++] = elementData[r]; } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. if (r != size) { System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; } if (w != size) { // clear to let GC do its work for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w; size = w; modified = true; } } return modified; }}

August 27, 2021 · 7 min · jiezi

关于arraylist:ArrayList源码

概述(1)ArrayList 是一种变长的汇合类,基于定长数组实现。 (2)ArrayList 容许空值和反复元素,增加元素时,会扩容机制生成一个更大的数组。 (3)能够保障在 O(1) 复杂度下实现随机查找操作。 (4)ArrayList 是非线程安全类。 为谋求效率,ArrayList没有实现同步(synchronized),如果须要多个线程并发拜访,用户能够手动同步,也可应用Vector代替。类的属性 /** * 默认初始容量 */private static final int DEFAULT_CAPACITY = 10;/** * 共享的空数组实例,用于空实例 */private static final Object[] EMPTY_ELEMENTDATA = {};/** *ArrayList 理论数据存储的一个数组 *存储ArrayList的元素的数组缓冲区。 ArrayList的容量是此数组缓冲区的长度。 *增加第一个元素时,任何具备elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList都将扩大为DEFAULT_CAPACITY。 Object[]数组,也就是说该数组能够放任何对象(所有对象都继承自父类Object) */transient Object[] elementData;/** * 共享的空数组实例,用于默认大小的空实例 */private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};/** * elementData 的大小,size是逻辑长度,并不是数组长度。 */private int size;//很有意思,上面介绍protected transient int modCount = 0;static润饰的变量,常驻于办法区,咱们不须要new,JVM会提前给咱们初始化好,这个个性在理论开发过程中,常常拿来做缓存。 ArrayList new的时候思考了缓存,为了防止咱们重复的创立无用数组,所有新new进去的ArrayList底层数组都指向缓存在办法区里的Object[]数组。 构造方法 //参数为初始化容量public ArrayList(int initialCapacity) { //判断容量的合法性 if (initialCapacity > 0) { //elementData才是理论寄存元素的数组 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { //如果传递的长度为0,就是间接应用本人曾经定义的成员变量(一个空数组) this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); }} //无参结构,应用默认的size为10的空数组,在构造方法中没有对数组长度进行设置,会在后续调用add办法的时候进行扩容public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;} //将一个参数为Collection的汇合转变为ArrayList(实际上就是将汇合中的元素换为了数组的模式)。//传入的汇合为null会抛出空指针异样(调用c.toArray()办法的时候)public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { //c.toArray()可能不会正确地返回一个 Object[]数组,那么应用Arrays.copyOf()办法 if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { //如果汇合转换为数组之后数组长度为0,就间接应用本人的空成员变量初始化elementData this.elementData = EMPTY_ELEMENTDATA; }} ...

August 3, 2021 · 3 min · jiezi

关于arraylist:3秒搞定ArrayList

Jdk8 总结 扩容和删除 ArrayList 就是一个实现了List接口的可主动扩容的数组,当增加元素的时候它会尝试扩容,扩容的规范是变为原来的1.5倍**,当删除元素的时候,它会左移元素,防止数组呈现"空位"容量 new ArrayList<>() 初始化容量为0,等到第一次add的时候再初始化为10有序汇合能够存储反复值和null值 示例: public static void main(String[] args) { List<String> a=new ArrayList<>(); a.add(null); a.add(null); a.add(null); System.out.println(a.size()); } 输入: 3ArrayList 是疾速失败的,在遍历的同时当汇合被批改后会抛出ConcurrentModificationException,能够应用Iterator 的删除办法来防止这个问题非线程平安的,如果你想在多线程环境中应用,能够应用Vector 或者它的线程平安包装类扩大 操作系统的局部性原理,数组的间断存储空间的个性充沛应用了局部性原理,也就是说硬件的高速缓存减速了数组的拜访性能Adding an element- 如果你应用的是  add(E e) 办法增加一个元素到ArrayList开端 ,它的工夫复杂度 O(1);然而当空间有余引发扩容的时候,会导致新建数组而后拷贝数据,这个时候它的工夫复杂度 O(n) ;当你应用 add(int index, E element)的时候它的算法复杂度是 O(n - index) 也就是 O(n)Retrieving an element- 当你应用get(int index) 的时候,它的工夫复杂度是 O(1),因为数组能够间接依据下标进行定位Removing an element- 当你应用 remove(int index) 它的工夫复杂度是 O(n - index) ,因为它波及到挪动元素Traverse - 遍历的工夫工夫复杂度是O(n),也就是依赖于Capacity 的大小,如果你比拟器重遍历的性能,就请不要不要给它设置一个很大的初始容量UML底层是一个Object[],增加到ArrayList中的数据保留在了elementData属性中。 当调用new ArrayList<>()时,将一个空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA  赋值给了elementData,这个时候汇合的长度size为默认长度0例如当调用new ArrayList<>(100)时,依据传入的长度,new一个Object[100]赋值给elementData,当然如果玩儿的话,传了一个0,那么将一个空数组 EMPTY_ELEMENTDATA 赋值给了elementData例如当调用new ArrayList<>(new HashSet())时,依据源码,咱们可知,能够传递任何实现了Collection接口的类,将传递的汇合调用toArray()办法转为数组内赋值给elementData构造方法 ...

June 15, 2021 · 2 min · jiezi

关于arraylist:8-arraylist

arraylist线程不平安底层是数组反对快速访问randomAccess尾部有空余空间在尾减少删除工夫复杂度都是O(1),在指定地位减少删除工夫复杂度是O(n),因为要一个一个挪JDK7 new无参结构的ArrayList对象时,间接创立了长度是10的Object[]数组elementData 。jdk7中的ArrayList的对象的创立相似于单例的饿汉式,而jdk8中的ArrayList的对象的创立相似于单例的懒汉式。 扩容过程未指定初始容量的arraylist创立进去,容量是0add元素进来时,判断外部容量是否足够(外部容量>以后size+1)判断外部容量是否足够n时,如果arraylist是空的,将容量裁减到max(10,n),如果arraylist不是空的,看看容量够不够n,不够就扩容到n扩容到n时,利用位运算将旧容量扩容到1.5倍,新容量=max(1.5倍旧容量,n)。如果max(1.5倍旧容量,n)>maxInteger-8 && n>maxInteger-8,新容量=maxInteger如果max(1.5倍旧容量,n)>maxInteger-8 && n<maxInteger-8,新容量=maxInteger-8 java 中的 length属性是针对数组说的,比如说你申明了一个数组,想晓得这个数组的长度则用到了 length 这个属性.java 中的 length() 办法是针对字符串说的,如果想看这个字符串的长度则用到 length() 这个办法.java 中的 size() 办法是针对泛型汇合说的,如果想看这个泛型有多少个元素,就调用此办法来查看! 在此列表中的指定地位插入指定的元素。先调用 rangeCheckForAdd 对index进行界线查看;而后调用 ensureCapacityInternal 办法保障capacity足够大;再将从index开始之后的所有成员后移一个地位;将element插入index地位;最初size加1。 System.arraycopy()是一个 native 办法,用来add的时候一个一个挪 /*** 复制数组* @param src 源数组* @param srcPos 源数组中的起始地位* @param dest 指标数组* @param destPos 指标数组中的起始地位* @param length 要复制的数组元素的数量*/public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); 把src数组,在第srcPos地位插入新数据,后边的挨个挪一个 Arrays.copyOf()办法 申请一个新的数组。调用System.arraycopy,将源数组中的数据进行拷贝,并返回新的数组 ensureCapacity办法,如有必要,减少此 ArrayList 实例的容量,以确保它至多能够包容由minimum capacity参数指定的元素数。最好在 add 大量元素之前用 ensureCapacity 办法,以缩小增量重新分配的次数 ...

June 7, 2021 · 1 min · jiezi

关于arraylist:Java经典笔试题可以手写一个ArrayList的简单实现吗

面试官Q1:能够手写一个ArrayList的简略实现吗? 咱们都晓得ArrayList是基于数组实现,如果让你实现JDK源码ArrayList中add()、remove()、get()办法,你晓得如何实现吗?这一节,咱们不看源码,咱们想想如何简略的实现ArrayList几个根本办法? 确定数据结构咱们晓得,ArrayList中无论什么数据都能放,是不是意味着它是一个Object类型,既然是数组,那么是不是Object[]数组类型的?所以咱们定义的数据结构如下: private Object[] elementData; private int size;设置自定义的MyArrayList的长度为10 public MyArrayList(){ this(10); } public MyArrayList(int initialCapacity){ if(initialCapacity<0){ try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); } } elementData = new Object[initialCapacity]; }有了存放数据的地位,接下来,咱们想想如何将数据放入数组? 增加数据public void add(Object obj){ elementData[size++]=obj;}每增加一个元素,size就会自增1,咱们定义的数组长度为10,当咱们增加到11个元素的时候,显然没有中央寄存新增加的数据,这个时候咱们须要对数组进行扩容解决 对下面代码做如下批改: public void add(Object obj){ if(size==elementData.length){ //创立一个新的数组,并且这个数组的长度是原数组长度的2倍 Object[] newArray = new Object[size*2]; //应用底层拷贝,将原数组的内容拷贝到新数组 System.arraycopy(elementData, 0, newArray, 0, elementData.length); //并将新数组赋值给原数组的援用 elementData = newArray; } //新来的元素,间接赋值 elementData[size++]=obj;}用一张图来示意就是这样的: ...

December 4, 2020 · 1 min · jiezi

关于arraylist:一文看懂ArrayList

前言很久之前写过一篇无关HashMap的文章:一文看懂HashMap,反应不错。原本手前面是想写篇文章来介绍ArrayList,起初事件多就忘了,明天就来好好聊聊ArrayList。 注释ArrayList相比HashMap来说就比较简单了,先来看看实现了哪些接口: public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ //默认容量大小 private static final int DEFAULT_CAPACITY = 10;//指定ArrayList容量为0时返回该数组private static final Object[] EMPTY_ELEMENTDATA = {};//当没有指定ArrayList容量时返回该数组private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//保留增加到ArrayList中的元素(ArrayList的底层其实就是一个数组)transient Object[] elementData; //ArrayList中元素数量private int size;.....}从下面的源码咱们能够提取出上面几个要害信息: ArrayList底层应用一个Object类型的数组来保留数据ArrayList默认大小为10能够看到ArrayList底层是应用一个数组elementData来保留数据的,因而也被称为动静数组。值得注意的是用了elementData用了transient润饰。 transient示意elementData无奈被序列化,为什么要这么设置呢? 因为elementData外面不是所有的元素都有数据,因为容量的问题,elementData外面有一些元素是空的,这种是没有必要序列化的。ArrayList的序列化和反序列化依赖writeObject和readObject办法来实现。能够防止序列化空的元素。 接下来咱们从罕用构造函数动手: public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}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); }}下面两个构造函数比较简单,就是给对数组做初始化,这里不再细说。 ...

October 20, 2020 · 3 min · jiezi

关于arraylist:源码分析ArrayList和LinkedList如何实现的我看你还有机会

文章曾经收录在 Github.com/niumoo/JavaNotes ,更有 Java 程序员所须要把握的外围常识,欢送Star和指教。 欢送关注我的公众号,文章每周更新。前言说真的,在 Java 应用最多的汇合类中,List 相对占有一席之地的,它和 Map 一样实用于很多场景,十分不便咱们的日常开发,毕竟存储一个列表的需要随处可见。尽管如此,还是有很多同学没有弄明确 List 中 ArrayList 和 LinkedList 有什么区别,这几乎太遗憾了,这两者其实都是数据结构中的根底内容,这篇文章会从根底概念开始,剖析两者在 Java 中的具体源码实现,寻找两者的不同之处,最初思考它们应用时的注意事项。 这篇文章会蕴含以下内容。 介绍线性表的概念,具体介绍线性表中数组和链表的数据结构。进行 ArrayList 的源码剖析,比方存储构造、扩容机制、数据新增、数据获取等。进行 LinkedList 的源码剖析,比方它的存储构造、数据插入、数据查问、数据删除和 LinkedList 作为队列的应用形式等。进行 ArrayList 和 LinkedList 的总结。线性表要钻研 ArrayList 和 LinkedList ,首先要弄明确什么是线性表,这里援用百度百科的一段文字。 线性表是最根本、最简略、也是最罕用的一种数据结构。线性表(linear list)是数据结构的一种,一个线性表是n个具备雷同个性的数据元素的无限序列。你必定看到了,线性表在数据结构中是一种最根本、最简略、最罕用的数据结构。它将数据一个接一个的排成一条线(可能逻辑上),也因而线性表上的每个数据只有前后两个方向,而在数据结构中,数组、链表、栈、队列都是线性表。你能够设想一下整整齐齐排队的样子。 看到这里你可能有疑难了,有线性表,那么必定有非线性表喽?没错。二叉树和图就是典型的非线性构造了。不要被这些花里胡哨的图吓到,其实这篇文章非常简单,心愿同学急躁看完点个赞。 数组既然晓得了什么是线性表,那么了解数组也就很容易了,首先数组是线性表的一种实现。数组是由雷同类型元素组成的一种数据结构,数组须要调配一段间断的内存用来存储。留神关键词,雷同类型,间断内存,像这样。 不好意思放错图了,像这样。 下面的图能够很直观的体现数组的存储构造,因为数组内存地址间断,元素类型固定,所有具备疾速查找某个地位的元素的个性;同时也因为数组须要一段间断内存,所以长度在初始化长度曾经固定,且不能更改。Java 中的 ArrayList 实质上就是一个数组的封装。 链表链表也是一种线性表,和数组不同的是链表不须要间断的内存进行数据存储,而是在每个节点里同时存储下一个节点的指针,又要留神关键词了,每个节点都有一个指针指向下一个节点。那么这个链表应该是什么样子呢?看图。 哦不,放错图了,是这样。 上图很好的展现了链表的存储构造,图中每个节点都有一个指针指向下一个节点地位,这种咱们称为单向链表;还有一种链表在每个节点上还有一个指针指向上一个节点,这种链表咱们称为双向链表。图我就不画了,像上面这样。 能够发现链表不用间断内存存储了,因为链表是通过节点指针进行下一个或者上一个节点的,只有找到头节点,就能够以此找到前面一串的节点。不过也因而,链表在查找或者拜访某个地位的节点时,须要O(n)的工夫复杂度。然而插入数据时能够达到O(1)的复杂度,因为只须要批改节点指针指向。 ArratList下面介绍了线性表的概念,并举出了两个线性表的理论实现例子,既数组和链表。在 Java 的汇合类 ArrayList 里,实际上应用的就是数组存储构造,ArrayList 对 Array 进行了封装,并减少了不便的插入、获取、扩容等操作。因为 ArrayList 的底层是数组,所以存取十分迅速,然而增删时,因为要挪动前面的元素地位,所以增删效率绝对较低。那么它具体是怎么实现的呢?无妨深刻源码一探到底。 ArrayList 存储构造查看 ArrayList 的源码能够看到它就是一个简略的数组,用来数据存储。 ...

August 13, 2020 · 6 min · jiezi

深入剖析ArrayList源码

概念ArrayList是一个其容量能够动态增长的动态数组 继承关系图: 源码我们从源码角度看一下: public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{//默认容量大小 private static final int DEFAULT_CAPACITY = 10;//指定ArrayList容量为0时返回该数组private static final Object[] EMPTY_ELEMENTDATA = {};//当没有指定ArrayList容量时返回该数组private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//存放数据transient Object[] elementData; //ArrayList中元素数量private int size;总结 ArrayList的默认容量为10ArrayList的底层其实就是一个数组,用elementData数组存放数据注意:可以看到elementData被transient标识,代表elementData无法被序列化,为什么要这么设置呢?因为elementData里面不是所有的元素都有数据,因为容量的问题,elementData里面有一些元素是空的,这种是没有必要序列化的。ArrayList的序列化和反序列化依赖writeObject和readObject方法来实现。可以避免序列化空的元素。 构造器//构造一个初始容量为10的空数组public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}//构造一个具有初始容量值的空数组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); }}//构造一个包含指定元素的数组public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray 可能不会返回Object[](注释是这样说的),所以这里要判断一下类型 if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { //如果传入的c长度为0,则替换成空数组 this.elementData = EMPTY_ELEMENTDATA; }}构造函数很简单,这里就不多说了! ...

October 6, 2019 · 5 min · jiezi

ArrayList

概述ArraysList可以动态分配数组 ArrayList<...> list = new ArrayList(); <>内是泛型。泛型:集合中的所有元素都是统一的类型。泛型只能是引用类型,不能是基本类型。原因是集合里保存的是地址值,基本类型中没有地址值 ArrayList<int> list = new ArrayList();//错误ArrayList<String> list = new ArrayList();//正确ArrayList<Employee> list = new ArrayList();//正确List<Employee> list = new ArrayList();//多态,正确如果希望向集合ArrayList当中存储基本类型数据,必须使用基本类型对应的“包装类” ArrayList<Integer> list = new ArrayList();//正确ArrayList常用方法:add:添加元素到数组中。可以带索引ensureCapacity:array.ensureCapacity(100)将分配一个包含100个对象的内部数组,然后调用100次add。也可以ArrayList<Integer> array = new ArrayList<>(100),两者作用相同。size:返回数组列表中包含的实际元素数量trimToSize:当确定数组列表的大小不再发生变化,该方法将存储区域的大小调整为当前元素数量所需要的存储空间数目。垃圾回收器将回收多余的存储空间get和set:实现访问和改变数组元素的操作。set只能设置已存在的元素 remove:删除一个元素

July 8, 2019 · 1 min · jiezi

常见的集合容器应当避免的坑

前言前不久帮同事一起 review 一个 job 执行缓慢的问题时发现不少朋友在撸码实现功能时还是有需要细节不够注意,于是便有了这篇文章。 ArrayList 踩坑List<String> temp = new ArrayList() ;//获取一批数据List<String> all = getData();for(String str : all) { temp.add(str);}首先大家看看这段代码有什么问题嘛? 其实在大部分情况下这都是没啥问题,无非就是循环的往 ArrayList 中写入数据而已。 但在特殊情况下,比如这里的 getData() 返回数据非常巨大时后续 temp.add(str) 就会有问题了。 比如我们在 review 代码时发现这里返回的数据有时会高达 2000W,这时 ArrayList 写入的问题就凸显出来了。 填坑指南大家都知道 ArrayList 是由数组实现,而数据的长度有限;需要在合适的时机对数组扩容。 这里以插入到尾部为例 add(E e)。 ArrayList<String> temp = new ArrayList<>(2) ;temp.add("1");temp.add("2");temp.add("3");当我们初始化一个长度为 2 的 ArrayList ,并往里边写入三条数据时 ArrayList 就得扩容了,也就是将之前的数据复制一份到新的数组长度为 3 的数组中。 之所以是 3 ,是因为新的长度=原有长度 * 1.5通过源码我们可以得知 ArrayList 的默认长度为 10. 但其实并不是在初始化的时候就创建了 DEFAULT_CAPACITY = 10 的数组。 ...

July 4, 2019 · 2 min · jiezi

JDK源码那些事儿之常用的ArrayList

前面已经讲解集合中的HashMap并且也对其中使用的红黑树结构做了对应的说明,这次就来看下简单一些的另一个集合类,也是日常经常使用到的ArrayList,整体来说,算是比较好理解的集合了,一起来看下 前言jdk版本:1.8类定义public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable继承了AbstractList,实现了List,提供对数组队列的增删改查操作实现RandomAccess接口,提供随机访问功能实现Cloneable接口,提供克隆功能实现Serializable接口,支持序列化,方便序列化传输 变量说明 private static final long serialVersionUID = 8683452581122892189L; /** * 默认的初始化容量 * 这里和HashMap初始容量不同,默认10 * 有些面试官可能问,虽然我感觉没必要记这玩意 */ private static final int DEFAULT_CAPACITY = 10; /** * 空集合,在构造函数中看说明 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 默认容量大小的空集合,这里和上边一样,但是第一次添加的时候会自动扩容到默认容量,看构造函数的说明 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. * * 基于数组实现容量大小变化,上边注释也说了第一次添加元素时,将容量扩展到DEFAULT_CAPACITY * 更详细的接着往下看 */ transient Object[] elementData; // non-private to simplify nested class access /** * 数组长度,即arraylist的长度 */ private int size; /** * 最大数组长度限制 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;从上边变量定义也能看出来ArrayList本质上是基于Object[]实现,故方法上的操作都是基于数组来进行 ...

May 11, 2019 · 7 min · jiezi

ArrayList-线程安全性学习

引言最近学校的氛围比较活跃,考研的复习,不考研的都在写简历准备面试。 看了看,最近也没有好公司来办宣讲会,也就没了投简历的意向。最近看了看面试题,想着补一补基础,以后面几家Spring Cloud的企业,去和面试官交流交流。 Spring Cloud的学习与体会 最近看了《Spring Cloud微服务实战》一书,感觉受益匪浅,大有裨益。 高并发应用,必须是要启用Spring Cloud的。有了Spring Cloud,就不用再像之前一样,前端工程师团队,后端工程师团队,运维团队。而是按模块划分,订单模块团队,支付模块团队,每个团队里都是从前端到后台到运维的全栈工程师。 就像上次黄庭祥说的,ThinkPHP开发,他写学期管理;AngularJS开发,他又写学期管理;Angular开发,他还写学期管理。想到什么了么?肯定精通这个模块的业务逻辑啊? 如果培养出优秀的支付模块团队、优秀的安全模块团队、优秀的高并发优化团队,其实淘宝也不过如此。 相互的依赖,从原来的@Autowired转为服务器接口间的调用。每个模块都是一个Spring Cloud应用,各应用间通过互相调用、相互协作共同实现业务功能,同时,各应用模块可以采用不同的数据库,以发挥各数据库之所长。 然后后台分布式部署,到了并发的时候,给相应的模块加服务器负载均衡就是了。个人中心模块,不常用,两个服务器负载;订单模块,可能会并发,加个百十来个服务器负载均衡。当然,像618、双十一这样的场景,肯定不是加服务器就能解决的,我这里只是举个简单的例子。模块划分之后,可以有针对性地解决高并发问题。 不扯淡了,开始进入正题。 面试题再谈线程安全什么是线程安全? 我看到这道题就感觉怎么也说不出来,就是多线程的环境下运行,我这个应用也不炸,虽然是这个意思,但是也不能这样回答啊?一时之间,找不到相关的学术词汇回答此问题。 这是想了许久后,我自己总结出的回答: 程序在单线程环境下正常执行得到了正确的结果,在多个线程并发执行的环境条件下,仍然能得到像单线程一样正确的结果,这就是线程安全。 如果一个类(或对象),我们在使用时,无需考虑任何多线程相关的问题,就像单线程一样使用,且最后能得到正确的结果,那就说这个类(或对象)是线程安全的。 ArrayList线程安全吗?看了许多面试题,发现面试官都喜欢以一个小方面进行切入,然后无限扩展,直到把面试者问懵圈为止。 ArrayList线程安全吗? 虽然天天用ArrayList,但是真的没考虑过这个问题。其实,ArrayList线程不安全。 ArrayList是一个内部采用数组实现的线性表,它相比数组最大的优点就是使用时可以不用去像数组一样new的时候去考虑要容纳多少个元素。ArrayList默认构造一个容量为10的数组。 private static final int DEFAULT_CAPACITY = 10;如果容量不够了,ArrayList会自动扩容,扩容至原来的1.5倍。(右移一位,相当于除以2)。 int newCapacity = oldCapacity + (oldCapacity >> 1);ArrayList没有对多线程问题进行处理,举个add方法的例子就能证明它线程不安全。 elementData[size++] = e;别看这是一行,其实是执行了两步操作,赋值和自增。 线程A add一个元素,然后暂停执行,size还没自增,然后线程B再add元素,size没变,就直接把A add的元素覆盖了。 不安全为什么要使用?又回到了之前向晨澍请教的问题,线程安全,必然是有额外开销的。 所以List的三个接口ArrayList、LinkedList和Vector。 线程不安全的要比线程安全的执行效率高。所以我们常用的是线程不安全的ArrayList、LinkedList,而从来没有用过线程安全的Vector。 Vector自JDK1.0就存在,设计得不够完善,多线程情况下如果使用不当也会发生错误,不推荐使用。 如何解决线程不安全既然Vector不能用,那我就想要一个线程安全的List得怎么整呢? 调用Collections.synchronizedList方法,使ArrayList线程安全。 List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());返回SynchronizedList类的对象,经典的装饰器模式,对方法访问加了同步。 public void add(int index, E element) { synchronized (mutex) {list.add(index, element);}}public E remove(int index) { synchronized (mutex) {return list.remove(index);}}总结何处望神州?满眼风光北固楼。千古兴亡多少事?悠悠。不尽长江滚滚流。年少万兜鍪,坐断东南战未休。天下英雄谁敌手?曹刘。生子当如孙仲谋。 ...

May 11, 2019 · 1 min · jiezi

ArrayList常见问题

简介ArrayList使用Object数组存储数组元素,并使用size属性记录数组长度。需要注意ArrayList是非线程安全的。常见问题汇总ArrayList的默认初始长度是多少?最大长度是多少?ArrayList的默认初始长度是10,是由DEFAULT_CAPACITY设定的。由于ArrayList底层是用Object数组存储元素,所以ArrayList最大长度为Integer.MAX_VALUE,即2147483647(2)。这里需要注意常量MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)并不是ArrayList真正的最大长度,原因可以参考hugeCapacity()方法。ArrayList是如何扩容的?扩容发生在插入数组元素时(关键方法为grow()方法)先计算增加完新元素后的ArrayList长度size然后size与minCapacity比较来判断是否需要扩容扩容时一般扩容为新数组长度newCapacity为原数组长度oldCapacity的1.5倍(oldCapacity带符号右移1位并加上oldCapacity)。特殊情况是存在newCapacity长度超过Array的最大支持长度MAX_ARRAY_SIZE则调用hugeCapacity()进行特殊处理防止数组超出最大长度(int最大值)。ArrayList扩容后是否会自动缩容?如果不能怎样进行缩容?ArrayList只能自动扩容,不能自动缩容。如果需要进行缩容,可以调用ArrayList提供的trimToSize()方法。ArrayList底层数组扩容时是如何保证高效复制数组的?表面上是调用Arrays.copyOf()方法,实际上是Arrays.copyOf()通过调用System.arraycopy()方法确保高效复制数组。

March 19, 2019 · 1 min · jiezi

让前端面试不再难(二)

昨天聊了一个算法题,今天接着聊!多聊几个。1、拍平数组(多维数组变成一维数组) let arr = [1,[2,3,[4],[5,6,[7]]],8]//[1,2,3,4,5,6,7,8] //这个有很多方法,我们一一说来 //第一种遍历数组,遍历过程遇到数组递归。 function flatten(arr, newArr) { //省去全局变量,还避开了函数嵌套闭包的形成。 newArr = newArr || [] for (let i = 0; i < arr.length; i++) { //如果是arr[i]是数组那么递归执行,并把当前arr[i]和已有newArr传进去继续push。 //如果不是直接push到newArr typeof arr[i] === ‘object’ ? flatten(arr[i], newArr) : newArr.push(arr[i]) } return newArr } console.log(flatten(arr)) //第二种,逻辑一样只不过遍历换成了reduce,如果读的比较困难请移步:https://segmentfault.com/a/1190000017510301 了解reduce function flatten1(arr) { return arr.reduce((newArr, item) => { return typeof item === ‘object’ ? newArr.concat(flatten1(item, newArr)) : (newArr.push(item), newArr) }, []) } console.log(flatten1(arr)) //第三种比较简单 function flatten2(arr) { //join会默认过滤数组内部[],算是一个奇淫技巧。 return arr.join(’,’).split(’,’) } console.log(flatten2(arr)) //第三种稍有点问题,如果数组内是number类型会拍平后会变成字符串。2、写一个方法判断字符串内()是否成对出现,是返回true不是返回false let str = ‘(()()())’ let str1 = ‘(())()())’ //1、先用栈的思路解决 function isTure(str, result = []) { let arr = str.split(’’) for (let i = 0; i < arr.length; i++) { const item = arr[i]; // 如果是左括号直接压栈 if (item === ‘(’) { // 压栈 result.push(item); // 如果是右括号且当前arr不为空弹出栈顶 } else if (item === ‘)’ && result.length != 0) { // 弹出栈顶 result.pop() } else { //如果是右括号且当前result为空,则直接判定为不合法 return false } } return result ? true : false } console.log(isTure(str)) //true console.log(isTure(str1)) //false 2、用计数方式其实和栈原理类似 function isTure1(str, count = 0) { let arr = str.split(’’) for (let i = 0; i < arr.length; i++) { const item = arr[i]; if (item === ‘(’) { count++ } else if (item === ‘)’ && count != 0) { count– } else { return false } } return !count ? true : false } console.log(isTure1(str))//true console.log(isTure1(str1))//falseok 今天分享就到这,明天继续! ...

January 25, 2019 · 2 min · jiezi