Android开发数据结构算法ArrayList源码详解

目录
  • 简介
  • ArrayList源码讲解
  • 初始化
  • 扩容
  • 增加元素
    • 一个元素
    • 一堆元素
  • 删除元素
    • 一个元素
    • 一堆元素
  • 修改元素
  • 查询元素
  • 总结
    • ArrayList优点
    • ArrayList的缺点

简介

ArrayList是List接口的一个实现类,它是一个集合容器,我们通常会通过指定泛型来存储同一类数据,ArrayList默认容器大小为10,自身可以自动扩容,当容量不足时,扩大为原来的1.5倍,和上篇文章的Vector的最大区别应该就是线程安全了,ArrayList不能保证线程安全,但我们也可以通过其他方式来实现线程安全。

ArrayList源码讲解

开头部分代码

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    @java.io.Serial
    //序列化uid
    private static final long serialVersionUID = 8683452581122892189L;
    //默认初始容量
    private static final int DEFAULT_CAPACITY = 10;
    //一个空对象
    private static final Object[] EMPTY_ELEMENTDATA = {};
    //一个空对象,如果使用默认构造函数创建,则默认对象内容默认是该值
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    //当前数据对象存放地方,当前对象不参与序列化
    transient Object[] elementData; // non-private to simplify nested class access
    //当前数组长度
    private int size;  //其他代码}

这部分可做出有关ArrayList的相关结构,如下图所示

初始化

  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() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    public ArrayList(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            elementData = EMPTY_ELEMENTDATA;
        }
    }  //此处为copyOf运行代码  
    public static <T> T[] copyOf(T[] original,
        int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());  }  
    public static <T,U> T[] copyOf(U[] original,
         int newLength,
         Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)        ? (T[])
        new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
        Math.min(original.length, newLength));
        return copy;  
         }

以上代码为ArrayList的初始化,分为三种方式:

1.为给定容量的初始化,传入参数为数组的初始化长度,当该参数大于等于0时,进行初始化,当该参数小于0时,抛出异常

2.过于简单

3.首先通过c.toArray()得到了集合c对应的数据数组,如果c也是ArrayList,直接将c.toArray()赋给elementData,而关于toArray的关键代码如上代码中关于copyOf的部分所示,从该段代码中可知此处的Arrays.copyOf调用的是三参数版本的函数。这个三参数的copyOf函数比较复杂,作用就是返回一个指定的newType类型的数组,这个数组的长度是newLength,值从original拷贝而来。拷贝的功能由System.arraycopy( )完成:如果newLength大于原数组的长度,多出来的元素初始化为null;如果小于原数组长度,将会进行截断操作。在这里,两参版本调用三参版本的三个参数为original, newLength, original.getClass(),故得到的数组元素类型和原数组类型一致,长度为newLength,数据由原数组复制而来。

总之,ArrayList的无参版toArray( )返回了一个和elemantData一模一样的拷贝数组。所以判断c也是ArrayList对象时,直接令elemantData 为c.toArray( )了。否则,会执行elementData = Arrays.copyOf(a, size, Object[].class)。经过上面的分析,三参的copyOf( )是返回一个数据内容和a一模一样的数组,但是数组类型转为Object[ ]类型。之所以有这条语句,猜想可能是某些集合的toArray( )方法,返回的数组不是Object[ ]类型,比方说用一个类继承ArrayList,并且重写toArray( )方法,让它返回一些别的类型。

扩容

    public void ensureCapacity(int minCapacity) {
        if (minCapacity > elementData.length
            && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
                 && minCapacity <= DEFAULT_CAPACITY)) {
            modCount++;
            grow(minCapacity);
        }
    }
  //核心部分
    private Object[] grow(int minCapacity) {
        int oldCapacity = elementData.length;
        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            int newCapacity = ArraysSupport.newLength(oldCapacity,
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity >> 1           /* preferred growth */);
            return elementData = Arrays.copyOf(elementData, newCapacity);
        } else {
            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
        }
    }
    private Object[] grow() {
        return grow(size + 1);
    }
  //newLength部分
    public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
        int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
        if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
            return prefLength;
        } else {
            // put code cold in a separate method
            return hugeLength(oldLength, minGrowth);
        }
    }
    private static int hugeLength(int oldLength, int minGrowth) {
        int minLength = oldLength + minGrowth;
        if (minLength < 0) { // overflow
            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;
        }
    }   public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;

关于扩容部分,private Object[] grow(int minCapacity)为核心部分,故我们先看这部分,if(oldCapacity > 0 || elementData !=DEFAULTCAPACITY_EMPTY_ELEMENTDATA)部分说明当该数组不是还未初始化的数组时,用newLength以及位运算来进行相应的扩容,而newLength相应的代码已经贴在了上面,让我们来进行具体分析:此处的minGrowth在grow部分为“minCapacity - oldCapacity”,即满足我们需要的容量,此处的prefGrowth在grow部分为“oldCapacity >> 1”,即原来容量的一半,当没有溢出时,扩容时所扩的容量就是这两者中最大的那个,而当溢出时,调用hugeLength()来满足minGrowth,如果还时溢出则最多只能给到 Integer.MAX_VALUE - 8 这个量。

那么当计算好长度之后,return elementData = Arrays.copyOf(elementData, newCapacity),即得到一个长度改变,元素类型和原数组相同的新数组。而当该数组不满足oldCapacity > 0 || elementData !=DEFAULTCAPACITY_EMPTY_ELEMENTDATA这个条件时,进行数组的初始化。

增加元素

一个元素

  //在尾部添加一个元素  
  private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)  //此处s为size的意思
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }
    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }  //在指定位置添加元素  
    public void add(int index, E element) {
        rangeCheckForAdd(index);    modCount++;
        final int s;    Object[] elementData;
        if ((s = size) == (elementData = this.elementData).length)
        elementData = grow();
        System.arraycopy(elementData, index,
        elementData, index + 1,                     s - index);
        elementData[index] = element;    size = s + 1; 
    }  
        private void rangeCheckForAdd(int index) {
            if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));  

        }

此处分为两个部分,一是向尾部添加一个元素的add(),如上面所示,当size与elementData.length相等时,说明数组中无多余空间进行添加,故进行扩容操作。将你要添加的值放入elementData[s]即数组的尾部,然后将size加一,这里的modCount是用来计算ArrayList中的结构性变化次数。

二是在指定位置添加元素的add(),如上面所示,首先对你想要添加的元素的位置进行判定

一堆元素

public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        modCount++;
        int numNew = a.length;
        if (numNew == 0)
            return false;
        Object[] elementData;
        final int s;
        //elementData剩余容量不足则进行扩容
        if (numNew > (elementData = this.elementData).length - (s = size))
            elementData = grow(s + numNew);
        //上文有提到的关于arraycopy的代码,此处为从数组尾部添加元素
        System.arraycopy(a, 0, elementData, s, numNew);
        size = s + numNew;
        return true;
    }
    public boolean addAll(int index, Collection<? extends E> c) {
        //上文提到的判定语句
        rangeCheckForAdd(index);
        Object[] a = c.toArray();
        modCount++;
        //要添加进来的元素的数量
        int numNew = a.length;
        if (numNew == 0)
            return false;
        Object[] elementData;
        final int s;
        if (numNew > (elementData = this.elementData).length - (s = size))
            elementData = grow(s + numNew);
        //计算要将多少元素向后移动
        int numMoved = s - index;
        if (numMoved > 0)
        //要在index位置,新增numNew个元素,所以从index位置开始,往后挪numNew位,一共有numMoved个元素需要挪动
            System.arraycopy(elementData, index,
                    elementData, index + numNew,
                    numMoved);
        //空出来的位置即为要添加的元素的位置
        System.arraycopy(a, 0, elementData, index, numNew);
        size = s + numNew;
        return true;
    }

关于添加一堆元素时的代码与之前的代码较为相似,故此处直接在代码中注释标出,应该很好理解。

删除元素

一个元素

public E remove(int index) {
        Objects.checkIndex(index, size);
        final Object[] es = elementData;
        @SuppressWarnings("unchecked") E oldValue = (E) es[index];
        fastRemove(es, index);
        return oldValue;
}
public staticint checkIndex(int index, int length) {    return Preconditions.checkIndex(index, length, null);}
private void fastRemove(Object[] es, int i) {
        modCount++;
        final int newSize;
        if ((newSize = size - 1) > i)
            System.arraycopy(es, i + 1, es, i, newSize - i);
        es[size = newSize] = null;
}//删除指定元素方法
public boolean remove(Object o) {    final Object[] es = elementData;    final int size = this.size;    int i = 0;    // 正序找对应元素下标    found: {        if (o == null) {            for (; i < size; i++)                if (es[i] == null)                    break found;        } else {            for (; i < size; i++)                if (o.equals(es[i]))                    break found;        }        return false;    }    // 找到了,调用fastRemove,进行删除    fastRemove(es, i);    return true;}

此处的删除是按照下标删,首先检查index是否合法,接着取到oldValue值也就是要删的元素值,然后调用fastRemove( )函数。在fastRemove里,首先自增modCount,再判断要删的元素是不是elemantData的第size个元素(也就是实际上的最后一个元素),如果是,不需要挪动元素操作,直接赋值为null即可,否则,还需要将删除位置之后的元素都往前挪一位。

当然也有个删除指定元素的方法,具体如上面**public boolean remove(Object o)**所示。

一堆元素

//删除集合c中的所有元素public boolean removeAll(Collection<?> c) {
        return batchRemove(c, false, 0, size);
    }//保留集合c中的所有元素
public boolean retainAll(Collection<?> c) {
        return batchRemove(c, true, 0, size);
    }//核心代码
boolean batchRemove(Collection<?> c, boolean complement,
                        final int from, final int end) {     //判断集合c是否为空
        Objects.requireNonNull(c);
        final Object[] es = elementData;
        int r;
        // Optimize for initial run of survivors
        for (r = from;; r++) {
            if (r == end)
                return false;
            if (c.contains(es[r]) != complement)
                break;
        }     //w用于写入保留的元素
        int w = r++;
        try {
            for (Object e; r < end; r++)
                if (c.contains(e = es[r]) == complement)
                    es[w++] = e;  //当这个元素可以保留时,将其赋给es[]
        } catch (Throwable ex) {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            System.arraycopy(es, r, es, w, end - r);
            w += end - r;
            throw ex;
        } finally {       //相关善后工作  
            modCount += end - w;
            shiftTailOverGap(es, w, end);
        }
        return true;
    }
private void shiftTailOverGap(Object[] es, int lo, int hi) {    //善后工作相关代码    System.arraycopy(es, hi, es, lo, size - hi);
   // 从索引hi开始,有size-hi个元素需要往左挪,这些元素依次挪到lo以及lo之后的位置
     // 它们都向左挪了hi-lo个单位
  // 挪动之后,原先的索引size-1的元素,对应的是size-1-(hi-lo),这个索引之后的元素都赋值为null    for (int to = size, i = (size -= hi - lo); i < to; i++)        es[i] = null;}

此处的关于多个元素的操作分为删除多个元素和保留多个元素,而这两个操作均需要调用 “batchRemove“ ,故进行关于其的具体分析。

“batchRemove“ 首先进行了变量”r“的声明,接着是一段for循环,而不管是“removeAll”还是“retainAll”都是from = 0 ,end = size,即从头到尾用r作为索引遍历数组,当c.contains(es[r]) != complement时break出去。对于“removeAll”,其complement为false,故当c.contains(es[r])为true的时候退出循环,即c中包含es[r],即找到了要删除的元素,此时的r为第一个要删除的元素的索引。

以此类推,对于“retainAll”,r为要保留的第一个元素的索引。关于后面w的部分,w是第一个要删的元素索引,找到要保留的元素,则把索引w的元素赋值为此元素,再自增w。这样子r一遍遍历完成后,要保留的元素也都向前移动好了。最后再进行善后工作,具体代码如上所示,详细过程已做注释。

修改元素

public E set(int index, E element) {
        Objects.checkIndex(index, size);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
public static
    int checkIndex(int index, int length) {
        return Preconditions.checkIndex(index, length, null);
    }
public static <X extends RuntimeException>
    int checkIndex(int index, int length,
                   BiFunction<String, List<Number>, X> oobef) {
        if (index < 0 || index >= length)
            throw outOfBoundsCheckIndex(oobef, index, length);
        return index;
    }

修改元素部分也与之前大同小异,首先对index是否合法进行判断,成功后再对这个元素的值进行修改,并返回旧值

查询元素

  public int indexOf(Object o) {
        return indexOfRange(o, 0, size);
    }
    int indexOfRange(Object o, int start, int end) {
        Object[] es = elementData;
        if (o == null) {
            for (int i = start; i < end; i++) {
                if (es[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = start; i < end; i++) {
                if (o.equals(es[i])) {
                    return i;
                }
            }
        }
        return -1;
    }

关于此处,首先是indexOf函数,就是根据元素找索引,调用”indexOfRange“,从左往右找,找到第一个索引就返回;在ArrayList中还有个lastIndexOf,与前面提到的查找正好相反,为从右往左找。

还有一些其他较为简单的函数,这里就不一一列出了,下一篇试着分析下迭代器。格式没怎么调。

以上就是Android开发中的部分技术点,属于数据结构与算法这块。Android开发需要进阶的东西有很多,我们该如何让进阶自己必须了解自己技术层在那个位置;

总结

ArrayList优点

  • ArrayList底层以数组实现,是一种随机访问模式,再加上它实现了
  • RandomAccess接口,因此查找也就是get的时候非常快。
  • ArrayList在顺序添加一个元素的时候非常方便,只是往数组里面添加了一个元素而已。
  • 根据下标遍历元素,效率高
  • 根据下标访问元素,效率高
  • 可以自动扩容,默认为每次扩容为原来的1.5倍

ArrayList的缺点

  • 插入和删除元素的效率不高
  • 根据元素的值查找元素的下标需要遍历整个元素数组,效率不高
  • 线程不安全

以上就是Android开发数据结构算法ArrayList源码详解的详细内容,更多关于Android数据结构算法ArrayList的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android Studio 3.0中mipmap-anydpi-v26是什么东东

    在Android Studio 3.0中一旦我们创建了一个项目,一个名为mipmap-anydpi-v26自动创建的文件夹在res文件夹下.它究竟能干什么?为什么我们需要这个?我们在开发时该如何利用它? 另外,在项目创建之后,还会在此文件夹中创建两个xml文件.为什么这些文件在mipmap文件夹中?根据我们的理解,所有xml文件是保存在drawable目录下而不是mipmap中的. Android Studio 3.0会为您的应用程序创建一个自适应图标,该图标仅在sdk 26中可用.启动图标应放

  • Android AIDL中Map参数传递的问题详解

    前言 AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言. 我们都知道aidl是支持map作为参数传递的,但前提是map不能是泛型并且数据类型必须是aidl所支持的String,int等的Map参数: interface IMyAidl { void test(Map<String,String> datas); } 本以为这样写就可以正常往下进行了,但是这样会有错,抛出如下异常: 上述错误中首先说明不知道如何

  • Android 数据结构全面总结分析

    前言 这次算一个总结,我们平时都会用到各种各样的数据结构,但是可能从未看过它们内部是如何去实现的.只有了解了它们内部大概的一个实现原理,才能在不同的场景中能选出最适合这个场景的数据结构. 虽然标题说是Android,但其实有一半是属于java的,由于涉及得比较多,所以打算分篇来写会比较好,我不会把全部的源码都进行分析,主要做的是分析一些能表现这些数据结构的特征. Collection 一切数据结构的最顶层,是一个接口,继承迭代器Iterable,主要是定义所有数据结构的公共行为,比如说boole

  • Android中的Bitmap的详细介绍

    Bitmap简介(摘抄于网络) 位图文件(Bitmap),扩展名可以是.bmp或者.dib.位图是Windows标准格式图形文件,它将图像定义为由点(像素)组成,每个点可以由多种色彩表示,包括2.4.8.16.24和32位色彩. 例如,一幅1024×768分辨率的32位真彩图片,其所占存储字节数为:1024×768×32/(8*1024)=3072KB 位图文件图像效果好,但是非压缩格式的,需要占用较大存储空间,不利于在网络上传送.jpg/png格式则恰好弥补了位图文件的缺点. 在Android

  • Android 图片Bitmap的剪切的示例代码

    一.什么是Android中的Bitmap Bitmap是Android系统中的图像处理的最重要类之一.用它可以获取图像文件信息,进行图像剪切.旋转.缩放等操作,并可以指定格式保存图像文件. 二.Bitmap的剪切基本操作 复制代码 代码如下: public static Bitmap createBitmap (Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter) 从原始位图剪切图像,这是一种高

  • Android Map数据结构全面总结分析

    目录 前言 Map ArrayMap TreeMap HashMap 总结 前言 上一篇讲了Collection.Queue和Deque.List或Set,没看的朋友可以去简单看看,这一篇主要讲和Map相关的数据结构. Map 上篇有介绍过,Map是另一种数据结构,它独立于Collection,它的是一个接口,它的抽象实现是AbstractMap,它内部是会通过Set来实现迭代器 Set<Map.Entry<K, V>> entrySet(); 是和Set有关联的,思想上主要以ke

  • Android开发数据结构算法ArrayList源码详解

    目录 简介 ArrayList源码讲解 初始化 扩容 增加元素 一个元素 一堆元素 删除元素 一个元素 一堆元素 修改元素 查询元素 总结 ArrayList优点 ArrayList的缺点 简介 ArrayList是List接口的一个实现类,它是一个集合容器,我们通常会通过指定泛型来存储同一类数据,ArrayList默认容器大小为10,自身可以自动扩容,当容量不足时,扩大为原来的1.5倍,和上篇文章的Vector的最大区别应该就是线程安全了,ArrayList不能保证线程安全,但我们也可以通过其

  • Android线程间通信Handler源码详解

    目录 前言 01. 用法 02.源码 03.结语 前言 在[Android]线程间通信 - Handler之使用篇主要讲了 Handler 的创建,发送消息,处理消息 三个步骤.那么接下来,我们也按照这三个步骤,从源码中去探析一下它们具体是如何实现的.本篇是关于创建源码的分析. 01. 用法 先回顾一下,在主线程和非主线程是如何创建 Handler 的. //主线程 private val mHandler: Handler = object : Handler(Looper.getMainLo

  • Android开发使用HttpURLConnection进行网络编程详解【附源码下载】

    本文实例讲述了Android开发使用HttpURLConnection进行网络编程.分享给大家供大家参考,具体如下: --HttpURLConnection URLConnection已经可以非常方便地与指定站点交换信息,URLConnection下还有一个子类:HttpURLConnection,HttpURLConnection在URLConnection的基础上进行改进,增加了一些用于操作HTTP资源的便捷方法. setRequestMethod(String):设置发送请求的方法 get

  • Android实现屏幕锁定源码详解

    最近有朋友问屏幕锁定的问题,自己也在学习,网上找了下也没太详细的例子,看的资料书上也没有有关屏幕锁定程序的介绍,下个小决心,自己照着官方文档学习下,现在做好了,废话不多说,先发下截图,看下效果,需要注意的地方会加注释,有问题的朋友可以直接留言,我们共同学习交流,共同提高进步!直接看效果图: 一:未设置密码时进入系统设置的效果图如下: 二:设置密码方式预览: 三:密码解密效果图 四:九宫格解密时的效果图 下面来简单的看下源码吧,此处讲下,这个小DEMO也是临时学习下的,有讲的不明白的地方请朋友直接

  • python目标检测SSD算法预测部分源码详解

    目录 学习前言 什么是SSD算法 ssd_vgg_300主体的源码 学习前言 ……学习了很多有关目标检测的概念呀,咕噜咕噜,可是要怎么才能进行预测呢,我看了好久的SSD源码,将其中的预测部分提取了出来,训练部分我还没看懂 什么是SSD算法 SSD是一种非常优秀的one-stage方法,one-stage算法就是目标检测和分类是同时完成的,其主要思路是均匀地在图片的不同位置进行密集抽样,抽样时可以采用不同尺度和长宽比,然后利用CNN提取特征后直接进行分类与回归,整个过程只需要一步,所以其优势是速度

  • python目标检测SSD算法训练部分源码详解

    目录 学习前言 讲解构架 模型训练的流程 1.设置参数 2.读取数据集 3.建立ssd网络. 4.预处理数据集 5.框的编码 6.计算loss值 7.训练模型并保存 开始训练 学习前言 ……又看了很久的SSD算法,今天讲解一下训练部分的代码.预测部分的代码可以参照https://blog.csdn.net/weixin_44791964/article/details/102496765 讲解构架 本次教程的讲解主要是对训练部分的代码进行讲解,该部分讲解主要是对训练函数的执行过程与执行思路进行详

  • Android开发使用URLConnection进行网络编程详解

    本文实例讲述了Android开发使用URLConnection进行网络编程.分享给大家供大家参考,具体如下: URL的openConnection()方法将返回一个URLConnection,该对象表示应用程序和URL之间的通信连接,程序可以通过URLConnection实例向该URL发送请求,读取URL引用的资源.通常创建一个和URL的连接,并发送请求,读取此URL引用的资源. 需要如下步骤: a)通过调用URL对象openConnection()方法来创建URLConnection对象 b)

  • Android开发Jetpack组件WorkManager用例详解

    目录 一.简介 二.导入 三.基本使用 3.1 定义后台任务 3.2 配置任务运行条件 3.2.1 只需执行一次的任务 3.2.2 周期性执行的任务 3.3 将任务传给 WorkManager 四.高级配置 4.1 设置任务延迟执行 4.2 给任务添加标签 4.3 取消任务 4.3.1 根据标签取消任务 4.3.2 根据 request 的 id 取消任务 4.3.3 取消所有任务 4.4 任务重试 4.5 监听任务结果 4.6 传递数据 4.7 链式任务 一.简介 WorkManager 用于

  • Android 带logo的二维码详解及实例

    Android 带logo的二维码详解及实例 好久没有写博客了,快元旦了公司的事情也不是很多,刚好和朋友一起出去玩玩,朋友是搞PHP的说到了每天在公司都是搞些什么二维码和微信支付的相关东西,因为上班的时间不忙,所以随便来搞下. 二维码(Quick Response Code),又称二维条码,它是用特定的几何图形按一定规律在平面(二维方向)上分布的黑白相间的图形,是所有信息数据的一把钥匙.在现代商业活动中,如果一个产品是不能通过二维码来进行访问什么的,显然是不成功的.用的比较多的生成二维码的jar

  • Java并发编程之ConcurrentLinkedQueue源码详解

    一.ConcurrentLinkedQueue介绍 并编程中,一般需要用到安全的队列,如果要自己实现安全队列,可以使用2种方式: 方式1:加锁,这种实现方式就是我们常说的阻塞队列. 方式2:使用循环CAS算法实现,这种方式实现队列称之为非阻塞队列. 从点到面, 下面我们来看下非阻塞队列经典实现类:ConcurrentLinkedQueue (JDK1.8版) ConcurrentLinkedQueue 是一个基于链接节点的无界线程安全的队列.当我们添加一个元素的时候,它会添加到队列的尾部,当我们

随机推荐