zoukankan      html  css  js  c++  java
  • [Java多线程]-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析

    Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作.

    一、Atomic包下的所有类如下表:

    类摘要
    AtomicBoolean 可以用原子方式更新的 boolean 值。
    AtomicInteger 可以用原子方式更新的 int 值。
    AtomicIntegerArray 可以用原子方式更新其元素的 int 数组。
    AtomicIntegerFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。
    AtomicLong 可以用原子方式更新的 long 值。
    AtomicLongArray 可以用原子方式更新其元素的 long 数组。
    AtomicLongFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新。
    AtomicMarkableReference<V> AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。
    AtomicReference<V> 可以用原子方式更新的对象引用。
    AtomicReferenceArray<E> 可以用原子方式更新其元素的对象引用数组。
    AtomicReferenceFieldUpdater<T,V> 基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。
    AtomicStampedReference<V> AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。

    二、AtomicInteger源码分析和基本的方法使用:

    Atomicinteger类中的方法列表:

    构造方法摘要
    AtomicInteger() 
              创建具有初始值 0 的新 AtomicInteger。
    AtomicInteger(int initialValue) 
              创建具有给定初始值的新 AtomicInteger。
    方法摘要
     int addAndGet(int delta) 
              以原子方式将给定值与当前值相加。
     boolean compareAndSet(int expect, int update) 
              如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
     int decrementAndGet() 
              以原子方式将当前值减 1。
     double doubleValue() 
              以 double 形式返回指定的数值。
     float floatValue() 
              以 float 形式返回指定的数值。
     int get() 
              获取当前值。
     int getAndAdd(int delta) 
              以原子方式将给定值与当前值相加。
     int getAndDecrement() 
              以原子方式将当前值减 1。
     int getAndIncrement() 
              以原子方式将当前值加 1。
     int getAndSet(int newValue) 
              以原子方式设置为给定值,并返回旧值。
     int incrementAndGet() 
              以原子方式将当前值加 1。
     int intValue() 
              以 int 形式返回指定的数值。
     void lazySet(int newValue) 
              最后设置为给定值。
     long longValue() 
              以 long 形式返回指定的数值。
     void set(int newValue) 
              设置为给定值。
     String toString() 
              返回当前值的字符串表示形式。
     boolean weakCompareAndSet(int expect, int update) 
              如果当前值 == 预期值,则以原子方式将该设置为给定的更新值。
    从类 java.lang.Number 继承的方法
    byteValueshortValue
    从类 java.lang.Object 继承的方法
    cloneequalsfinalizegetClasshashCodenotifynotifyAllwaitwaitwait

    AtomicInteger源码:

    /*
     * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     *
     */
    
    /*
     *
     *
     *
     *
     *
     * Written by Doug Lea with assistance from members of JCP JSR-166
     * Expert Group and released to the public domain, as explained at
     * http://creativecommons.org/publicdomain/zero/1.0/
     */
    
    package java.util.concurrent.atomic;
    import sun.misc.Unsafe;
    
    /**
     * An {@code int} value that may be updated atomically.  See the
     * {@link java.util.concurrent.atomic} package specification for
     * description of the properties of atomic variables. An
     * {@code AtomicInteger} is used in applications such as atomically
     * incremented counters, and cannot be used as a replacement for an
     * {@link java.lang.Integer}. However, this class does extend
     * {@code Number} to allow uniform access by tools and utilities that
     * deal with numerically-based classes.
     *
     * @since 1.5
     * @author Doug Lea
    */
    public class AtomicInteger extends Number implements java.io.Serializable {
        private static final long serialVersionUID = 6214790243416807050L;
    
        // setup to use Unsafe.compareAndSwapInt for updates
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        private static final long valueOffset;
    
        static {
          try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
          } catch (Exception ex) { throw new Error(ex); }
        }
    
        private volatile int value;
    
        /**
         * Creates a new AtomicInteger with the given initial value.
         *
         * @param initialValue the initial value
         */
        public AtomicInteger(int initialValue) {
            value = initialValue;
        }
    
        /**
         * Creates a new AtomicInteger with initial value {@code 0}.
         */
        public AtomicInteger() {
        }
    
        /**
         * Gets the current value.
         *
         * @return the current value
         */
        public final int get() {
            return value;
        }
    
        /**
         * Sets to the given value.
         *
         * @param newValue the new value
         */
        public final void set(int newValue) {
            value = newValue;
        }
    
        /**
         * Eventually sets to the given value.
         *
         * @param newValue the new value
         * @since 1.6
         */
        public final void lazySet(int newValue) {
            unsafe.putOrderedInt(this, valueOffset, newValue);
        }
    
        /**
         * Atomically sets to the given value and returns the old value.
         *
         * @param newValue the new value
         * @return the previous value
         */
        public final int getAndSet(int newValue) {
            for (;;) {
                int current = get();
                if (compareAndSet(current, newValue))
                    return current;
            }
        }
    
        /**
         * Atomically sets the value to the given updated value
         * if the current value {@code ==} the expected value.
         *
         * @param expect the expected value
         * @param update the new value
         * @return true if successful. False return indicates that
         * the actual value was not equal to the expected value.
         */
        public final boolean compareAndSet(int expect, int update) {
            return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
        }
    
        /**
         * Atomically sets the value to the given updated value
         * if the current value {@code ==} the expected value.
         *
         * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
         * and does not provide ordering guarantees, so is only rarely an
         * appropriate alternative to {@code compareAndSet}.
         *
         * @param expect the expected value
         * @param update the new value
         * @return true if successful.
         */
        public final boolean weakCompareAndSet(int expect, int update) {
            return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
        }
    
        /**
         * Atomically increments by one the current value.
         *
         * @return the previous value
         */
        public final int getAndIncrement() {
            for (;;) {
                int current = get();
                int next = current + 1;
                if (compareAndSet(current, next))
                    return current;
            }
        }
    
        /**
         * Atomically decrements by one the current value.
         *
         * @return the previous value
         */
        public final int getAndDecrement() {
            for (;;) {
                int current = get();
                int next = current - 1;
                if (compareAndSet(current, next))
                    return current;
            }
        }
    
        /**
         * Atomically adds the given value to the current value.
         *
         * @param delta the value to add
         * @return the previous value
         */
        public final int getAndAdd(int delta) {
            for (;;) {
                int current = get();
                int next = current + delta;
                if (compareAndSet(current, next))
                    return current;
            }
        }
    
        /**
         * Atomically increments by one the current value.
         *
         * @return the updated value
         */
        public final int incrementAndGet() {
            for (;;) {
                int current = get();
                int next = current + 1;
                if (compareAndSet(current, next))
                    return next;
            }
        }
    
        /**
         * Atomically decrements by one the current value.
         *
         * @return the updated value
         */
        public final int decrementAndGet() {
            for (;;) {
                int current = get();
                int next = current - 1;
                if (compareAndSet(current, next))
                    return next;
            }
        }
    
        /**
         * Atomically adds the given value to the current value.
         *
         * @param delta the value to add
         * @return the updated value
         */
        public final int addAndGet(int delta) {
            for (;;) {
                int current = get();
                int next = current + delta;
                if (compareAndSet(current, next))
                    return next;
            }
        }
    
        /**
         * Returns the String representation of the current value.
         * @return the String representation of the current value.
         */
        public String toString() {
            return Integer.toString(get());
        }
    
    
        public int intValue() {
            return get();
        }
    
        public long longValue() {
            return (long)get();
        }
    
        public float floatValue() {
            return (float)get();
        }
    
        public double doubleValue() {
            return (double)get();
        }
    
    }
    AtomicInteger

    其中的属性:

    private static final long serialVersionUID = 6214790243416807050L;
    
        // setup to use Unsafe.compareAndSwapInt for updates
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        private static final long valueOffset;
    
        static {
          try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
          } catch (Exception ex) { throw new Error(ex); }
        }
    
        private volatile int value;

    unsafe :java中的sun.misc.Unsafe包,提供了安全访问内存的方法。这些方法提供给java访问底层的JNI(java-native-interface),因为这些方法最终是调用c/c++实现。
    valueOffset:指向相对于对象起始位置的偏移量(内存中)可以理解为引用指向的内存,通过这个值可以去内存中查找某个引用在内存的值。
    value:引用的当前值[预期值E]

    方法示例:

    public class AtomTest {
        private static AtomicInteger num = new  AtomicInteger(0);
        public static void main(String[] args) {
            System.out.println(num.compareAndSet(0, 1));//比较并设置,用0与内存中的值比较,相等的话,修改值为1
            System.out.println(num.compareAndSet(0, 1));//比较并设置,用0与内存中的值比较,相等的话,修改值为1
            System.out.println(num.get());;//获取初值
            num.set(2);//设置初值,
            System.out.println(num.get());
            System.out.println(num.decrementAndGet());//获取值自减返回减1之后的值
            System.out.println(num.addAndGet(2));//获取值添加2然后返回添加后的值
            System.out.println(num.getAndIncrement());//获取值并且自增,返回的是自增前的值
            System.out.println(num.getAndAdd(5));
            System.out.println(num.getAndDecrement());
            System.out.println(num.getAndSet(100));
            System.out.println(num.get());
        }
    }

    运行结果:

    true
    false
    1
    2
    1
    3
    3
    4
    9
    8
    100

    初始化的时候赋值为0,调用从compareAndSwap(0,1)之后返回true,compareAndSwap比较并交换,比较结果相同则修改值并返回TRUE不通则返回FALSE。

    看看这个方法的源码:

    /**
         * Atomically sets the value to the given updated value
         * if the current value {@code ==} the expected value.
         *
         * @param expect the expected value
         * @param update the new value
         * @return true if successful. False return indicates that
         * the actual value was not equal to the expected value.
         */
        public final boolean compareAndSet(int expect, int update) {
            return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
        }

    compareAndSwap:当前引用值和与要更新的值以及对象,和偏移量做参数去更新内存,根据对象和偏移量可以找到引用的内存值与引用值(期望值)作比较,相等则说明这个值在此对象获得后未被修改,那么我们就可以更新,则结果为true。这个方法就是传说中的CAS,CAS相较于synchonrized提供了一种无锁的线程安全保证。

    示例中的前两个操作是一样的,我们可以理解为两个线程同时更新值为1,前一个快一点,发现更新的时候内存值未变则更新,等到第二个更新的时候原始值0与内存值(此时内存值变为1了)比较则失败返回FALSE并且不进行更新。这样就保证了一致性,而我们看到变量的值value使用voliate修饰的,保证了数据的内存可见性(一个线程修改其他线程可见)。所以多个线程并发的时候这个类代替Integer能够保证安全。

    这个操作非常像数据库的乐观锁,给每个表添加一个version版本号,修改数据后要持久化先查询看版本号一致不,一致的话则持久化,不一致则重新获取值进行修改。

    三、AtomicLong源码以及与AtomicInteger的不同:

    package java.util.concurrent.atomic;
    import sun.misc.Unsafe;
    
    /**
     * A {@code long} value that may be updated atomically.  See the
     * {@link java.util.concurrent.atomic} package specification for
     * description of the properties of atomic variables. An
     * {@code AtomicLong} is used in applications such as atomically
     * incremented sequence numbers, and cannot be used as a replacement
     * for a {@link java.lang.Long}. However, this class does extend
     * {@code Number} to allow uniform access by tools and utilities that
     * deal with numerically-based classes.
     *
     * @since 1.5
     * @author Doug Lea
     */
    public class AtomicLong extends Number implements java.io.Serializable {
        private static final long serialVersionUID = 1927816293512124184L;
    
        // setup to use Unsafe.compareAndSwapLong for updates
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        private static final long valueOffset;
    
        /**
         * Records whether the underlying JVM supports lockless
         * compareAndSwap for longs. While the Unsafe.compareAndSwapLong
         * method works in either case, some constructions should be
         * handled at Java level to avoid locking user-visible locks.
         */
        static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
    
        /**
         * Returns whether underlying JVM supports lockless CompareAndSet
         * for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS.
         */
        private static native boolean VMSupportsCS8();
    
        static {
          try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));
          } catch (Exception ex) { throw new Error(ex); }
        }
    
        private volatile long value;
    
        /**
         * Creates a new AtomicLong with the given initial value.
         *
         * @param initialValue the initial value
         */
        public AtomicLong(long initialValue) {
            value = initialValue;
        }
    
        /**
         * Creates a new AtomicLong with initial value {@code 0}.
         */
        public AtomicLong() {
        }
    
        /**
         * Gets the current value.
         *
         * @return the current value
         */
        public final long get() {
            return value;
        }
    
        /**
         * Sets to the given value.
         *
         * @param newValue the new value
         */
        public final void set(long newValue) {
            value = newValue;
        }
    
        /**
         * Eventually sets to the given value.
         *
         * @param newValue the new value
         * @since 1.6
         */
        public final void lazySet(long newValue) {
            unsafe.putOrderedLong(this, valueOffset, newValue);
        }
    
        /**
         * Atomically sets to the given value and returns the old value.
         *
         * @param newValue the new value
         * @return the previous value
         */
        public final long getAndSet(long newValue) {
            while (true) {
                long current = get();
                if (compareAndSet(current, newValue))
                    return current;
            }
        }
    
        /**
         * Atomically sets the value to the given updated value
         * if the current value {@code ==} the expected value.
         *
         * @param expect the expected value
         * @param update the new value
         * @return true if successful. False return indicates that
         * the actual value was not equal to the expected value.
         */
        public final boolean compareAndSet(long expect, long update) {
            return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
        }
    
        /**
         * Atomically sets the value to the given updated value
         * if the current value {@code ==} the expected value.
         *
         * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
         * and does not provide ordering guarantees, so is only rarely an
         * appropriate alternative to {@code compareAndSet}.
         *
         * @param expect the expected value
         * @param update the new value
         * @return true if successful.
         */
        public final boolean weakCompareAndSet(long expect, long update) {
            return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
        }
    
        /**
         * Atomically increments by one the current value.
         *
         * @return the previous value
         */
        public final long getAndIncrement() {
            while (true) {
                long current = get();
                long next = current + 1;
                if (compareAndSet(current, next))
                    return current;
            }
        }
    
        /**
         * Atomically decrements by one the current value.
         *
         * @return the previous value
         */
        public final long getAndDecrement() {
            while (true) {
                long current = get();
                long next = current - 1;
                if (compareAndSet(current, next))
                    return current;
            }
        }
    
        /**
         * Atomically adds the given value to the current value.
         *
         * @param delta the value to add
         * @return the previous value
         */
        public final long getAndAdd(long delta) {
            while (true) {
                long current = get();
                long next = current + delta;
                if (compareAndSet(current, next))
                    return current;
            }
        }
    
        /**
         * Atomically increments by one the current value.
         *
         * @return the updated value
         */
        public final long incrementAndGet() {
            for (;;) {
                long current = get();
                long next = current + 1;
                if (compareAndSet(current, next))
                    return next;
            }
        }
    
        /**
         * Atomically decrements by one the current value.
         *
         * @return the updated value
         */
        public final long decrementAndGet() {
            for (;;) {
                long current = get();
                long next = current - 1;
                if (compareAndSet(current, next))
                    return next;
            }
        }
    
        /**
         * Atomically adds the given value to the current value.
         *
         * @param delta the value to add
         * @return the updated value
         */
        public final long addAndGet(long delta) {
            for (;;) {
                long current = get();
                long next = current + delta;
                if (compareAndSet(current, next))
                    return next;
            }
        }
    
        /**
         * Returns the String representation of the current value.
         * @return the String representation of the current value.
         */
        public String toString() {
            return Long.toString(get());
        }
    
    
        public int intValue() {
            return (int)get();
        }
    
        public long longValue() {
            return get();
        }
    
        public float floatValue() {
            return (float)get();
        }
    
        public double doubleValue() {
            return (double)get();
        }
    
    }
    AtomicLong源码

    关于AtomicLong的用法和AtomicInteger类似:

    不同之处在于多了下面一部分:一个静态的Boolean值VM_SUPPORTS_LONG_CAS,调用一个 native方法VMSupportCS8()返回虚拟机是否支持Long类型的无锁CAS机制

     /**
         * Records whether the underlying JVM supports lockless
         * compareAndSwap for longs. While the Unsafe.compareAndSwapLong
         * method works in either case, some constructions should be
         * handled at Java level to avoid locking user-visible locks.
         */
        static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
    
        /**
         * Returns whether underlying JVM supports lockless CompareAndSet
         * for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS.
         */
        private static native boolean VMSupportsCS8();

    从这里可以看出不同的VM支持的数据类型可能有所差别,因为CAS需要硬件系统的支持。

    四、AtomicReference源码及使用:

    /**
     * An object reference that may be updated atomically. See the {@link
     * java.util.concurrent.atomic} package specification for description
     * of the properties of atomic variables.
     * @since 1.5
     * @author Doug Lea
     * @param <V> The type of object referred to by this reference
     */
    public class AtomicReference<V>  implements java.io.Serializable {
        private static final long serialVersionUID = -1848883965231344442L;
    
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        private static final long valueOffset;
    
        static {
          try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicReference.class.getDeclaredField("value"));
          } catch (Exception ex) { throw new Error(ex); }
        }
    
        private volatile V value;
    
        /**
         * Creates a new AtomicReference with the given initial value.
         *
         * @param initialValue the initial value
         */
        public AtomicReference(V initialValue) {
            value = initialValue;
        }
    
        /**
         * Creates a new AtomicReference with null initial value.
         */
        public AtomicReference() {
        }
    
        /**
         * Gets the current value.
         *
         * @return the current value
         */
        public final V get() {
            return value;
        }
    
        /**
         * Sets to the given value.
         *
         * @param newValue the new value
         */
        public final void set(V newValue) {
            value = newValue;
        }
    
        /**
         * Eventually sets to the given value.
         *
         * @param newValue the new value
         * @since 1.6
         */
        public final void lazySet(V newValue) {
            unsafe.putOrderedObject(this, valueOffset, newValue);
        }
    
        /**
         * Atomically sets the value to the given updated value
         * if the current value {@code ==} the expected value.
         * @param expect the expected value
         * @param update the new value
         * @return true if successful. False return indicates that
         * the actual value was not equal to the expected value.
         */
        public final boolean compareAndSet(V expect, V update) {
            return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
        }
    
        /**
         * Atomically sets the value to the given updated value
         * if the current value {@code ==} the expected value.
         *
         * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
         * and does not provide ordering guarantees, so is only rarely an
         * appropriate alternative to {@code compareAndSet}.
         *
         * @param expect the expected value
         * @param update the new value
         * @return true if successful.
         */
        public final boolean weakCompareAndSet(V expect, V update) {
            return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
        }
    
        /**
         * Atomically sets to the given value and returns the old value.
         *
         * @param newValue the new value
         * @return the previous value
         */
        public final V getAndSet(V newValue) {
            while (true) {
                V x = get();
                if (compareAndSet(x, newValue))
                    return x;
            }
        }
    
        /**
         * Returns the String representation of the current value.
         * @return the String representation of the current value.
         */
        public String toString() {
            return String.valueOf(get());
        }
    
    }
    AtomicReference源码

    除了类型是个对象的之外,方法使用和AtomicInteger一模一样:

    AtomicReference的使用:

    import java.util.concurrent.atomic.AtomicReference;
    
    public class AtomTest {
        private static AtomicReference<Pig> pigtest;
        public static void main(String[] args) {
            Pig pig = new Pig("猪坚强", 2);
            Pig pig2 = new Pig("猪八戒", 2);
            System.out.println("pig_hashCode:"+pig.hashCode());
            System.out.println("pig2_hashCode:"+pig2.hashCode());
            AtomTest.pigtest = new AtomicReference<Pig>(pig);
            System.out.println(pigtest.get().toString());
            System.out.println(pigtest.get().hashCode());
            System.out.println(pigtest.compareAndSet(pig, pig2));
            System.out.println(pigtest.compareAndSet(pig, pig2));
            System.out.println(pigtest.get().toString());
            System.out.println(pigtest.get().hashCode());
        }
    }

    运行结果:

    pig_hashCode:779824645
    pig2_hashCode:420110874
    [猪坚强,2]
    779824645
    true
    false
    [猪八戒,2]
    420110874

    第一步:先获取pigTest的toString(),和hashCode,结果是【猪坚强,2】,hashCode:779824645【我们发现hashCode居然==pig_hashCode】

    第二步:进行CAS修改为pig2结果为true,再进行一次结果为FALSE,

    第三步:获取pigTest的toString(),和hashCode,结果是【猪八戒,2】,hashCode:420110874【我们发现hashCode居然==pig2_hashCode】

    结果说明了:pigtest是一个指向对象引用的引用,利用CAS操作可以修改pigTest指向的对象引用从而改变自身指向的对象。第一次CAS成功将pigtest指向的pig修改成了pig2.第二次CAS操作失败了,可以保证多线程并发时的安全问题。

    五、总结:

      Atomic包下内容并不复杂,一句话来说就是提供了CAS无锁的安全访问机制。表现出来的是通过期望值E与内存值M作比较,相同则修改内存值M为更新值U。四个参数:当前对象this,偏移量V,期望值E,更新值U。

      利用CAS+voliate+native的机制保证数据操作的原子性,可见性和一致性。voliate使变量可见,CAS调用unsafe中的native方法访问系统底层的实现。unsafe中的这些方法直接操作内存,运用不当可能造成很大的问题。

      其实从Atomic包中的原子类的探索中,只是想引出CAS这个概念,CAS同样提供了一种线程安全的机制,而它不同于Synchonrized,synchonrized被称之为重量级锁,原因是因为粒度太强,加锁就代表着线程阻塞,高并发访问时带来的性能问题是硬伤。

      为了解决这种问题出现了两种机制:一种就是CAS,另一种是锁优化。

      CAS是将阻塞下降到了底层CPU上(纯属个人理解,因为看到有权威是说存在阻塞的,可能让别的线程知道已经更改了数据并且更新失败也是一种阻塞吧),语言层面访问效率远远低于系统内部硬件上,尽管同样是阻塞,在系统内部加锁解锁的效率要高很多。但是需要的是硬件支持,不过现在绝大部分CPU都已经支持CAS了。

      unsafe类:http://www.cnblogs.com/mickole/articles/3757278.html大家可以看这篇博客。

  • 相关阅读:
    LeetCode OJ String to Integer (atoi) 字符串转数字
    HDU 1005 Number Sequence(AC代码)
    HDU 1004 Let the Balloon Rise(AC代码)
    HDU 1003 Max Sum(AC代码)
    012 Integer to Roman 整数转换成罗马数字
    011 Container With Most Water 盛最多水的容器
    010 Regular Expression Matching 正则表达式匹配
    007 Reverse Integer 旋转整数
    006 ZigZag Conversion
    005 Longest Palindromic Substring 最长回文子串
  • 原文地址:https://www.cnblogs.com/NextNight/p/6600343.html
Copyright © 2011-2022 走看看