java集合-ArrayList

java集合

 


ArrayList

 

Iterable

 

Comparable(排序接口)

项目结构:

class Dog implements Comparable<Dog> {
    private String name;
    private int age;
​
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
​
    @Override
    public String toString() {
        return '\n'+"Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}'+'\n';
    }
​
    // 看你自己默认排序哪一个
    @Override
    public int compareTo(Dog o) {
        //调用Arrays.sort() 默认排序狗的年龄 从小到大排序
        return this.age - o.age;
    }
​
}

Comparator(比较器)

//ReverSortDogName.java
​
import java.util.Comparator;
// 想弄实例化的排序就要调用Comparator
class ReverSortDogName implements Comparator<Dog> {
    @Override
    public int compare(Dog o1, Dog o2) {
        return o2.getName().compareTo(o1.getName());
    }
}
import java.util.Arrays;
public class demoMain {
    public static void main(String[] args) {
​
        Dog[] dogs = new Dog[]{
                new Dog("111", 16),
                new Dog("333", 18),
                new Dog("222", 17)};
​
        //Arrays.sort(dogs);   //Comparable ,执行这个方法默认是按age从小到大排序
         Arrays.sort(dogs, new ReverSortDogName()); // Comparator,按name字符串编码从大到小排序
        System.out.println(Arrays.toString(dogs));
    }
}

默认输出 按年龄从小到大 按name编码从大到小 

CompareTo源码分析

   
 public int compareTo(String anotherString) {
        //获取两个需要比较的字符串的长度
        int len1 = value.length;
        int len2 = anotherString.value.length;
        //取两个字符串长度的最小值
        int lim = Math.min(len1, len2);
        //获取两个字符串的值
        char v1[] = value;
        char v2[] = anotherString.value;//(String底层使用char类型的数组保存类容)
        //从第一位开始,依次比较两个字符串每个字符的asc码
        int k = 0;
        while (k < lim) {
            char c1 = v1[k];//(举例:123)
            char c2 = v2[k];//(举例:124)
            //如果asc码不同,返回差值
            if (c1 != c2) {
                return c1 - c2;  //(举例:123-124=-1,从大到小排列)
            }
            k++;
        }
        //如果两个字符串的asc码比到最后,依然相同,则返回两个字符串长度的差值,举例(c1:123,c2:1234)
        return len1 - len2;
    }
/**
 * 正数:从小到大排列
 * 负数:从大到小排列
 * 相同:内容相同
 */
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
​
public class demoMain {
    public static void main(String[] args) {
        List<Integer> list1 = Arrays.asList(5, 3, 2, 4, 1);
        Collections.sort(list1);
        System.out.println(list1);//默认升序
​
        List<Integer> list2 = Arrays.asList(5, 3, 2, 4, 1);
        Collections.sort(list2, (a, b) -> b - a);
        System.out.println(list2);//自定义降序
    }
}

运行结果: 

集合遍历的方式

区别:

1:fori是通过下标访问。

2:foreach是通过容器的itrator的next()方法来迭代。

一:使用lambda表达式便利集合

import java.util.HashMap;
import java.util.HashSet;
public class demoMain {
    public static void main(String[] args) {
        //遍历set集合
        HashSet<String> books = new HashSet<>();
        books.add("第一本");
        books.add("第二本");
        books.add("第三本");
        books.forEach(str -> System.out.println("书名:"  + str));
        //遍历map集合
        HashMap<String,String> mac=new HashMap<>();
        mac.put("1","1");
        mac.put("2","2");
        mac.put("3","3");
        mac.forEach((s, s2) -> System.out.println(s+"="+s2) );
    }
}

运行结果: 

二:使用iterator遍历集合元素

import java.util.HashSet;
import java.util.Iterator;
​
public class demoMain {
    public static void main(String[] args) {
        HashSet<String> books = new HashSet<>();
        books.add("第一本");
        books.add("第二本");
        books.add("第三本");
        //迭代器遍历集合
        Iterator<String> iterator=books.iterator();
        while(iterator.hasNext()){
            //hasNext()判断是否有下一个元素
            System.out.println(iterator.hasNext());
            //next() 取出下一个元素
            System.out.println(iterator.next());
        }
    }
}

运行结果: 

remove

import java.util.ArrayList;
import java.util.List;
​
public class demoMain {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("北京");
        list.add("上海");
        list.add("广州");
        list.add("深圳");
        for (String s : list) {
            System.out.println(s);
            if ("北京".equals(s)) {
                list.remove(s);
            }
        }
    }
}

运行结果: 

说明:迭代器在使用的过程中,禁止修改迭代器中的内容,否则会怕抛出java.util.ConcurrentModificationException

fail-fast

即快速失败机制,它是java集合中的一种错误检测机制, 当单个或多个线程在结构上对集合进行改变时,就有可能产生fail-fast机制.

private class Itr implements Iterator<E> {
        // Android-changed: Add "limit" field to detect end of iteration.
        // The "limit" of this iterator. This is the size of the list at the time the
        // iterator was created. Adding & removing elements will invalidate the iteration
        // anyway (and cause next() to throw) so saving this value will guarantee that the
        // value of hasNext() remains stable and won't flap between true and false when elements
        // are added and removed from the list.
        protected int limit = ArrayList.this.size;

        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor < limit;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            /**
             * 通过代码得知,expectedModCount 这个值再对象创建时就被赋予了一个固定值modCont,
             * 所以expectedModCount 的值肯定是固定的,那问题就显而易见了,当迭代器遍历元素时, 
             * 如果modCount这个值发生了改变,那么就会报出ConcurrentModificationException异常
             */
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();//此处异常
            int i = cursor;
            if (i >= limit)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();//此处异常

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
                limit--;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

fail safe

fail safe机制的原理是会复制原集合的一份数据出来,然后操作复制后的数据,避免操作原数据.

使用 CopyOnWriteArrayListConcurrentHashMap无论改变值还是结构都不会报出ConcurrentModificationException

源码分析(以ArrayList为例子)

private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return(要返回的下一个元素的索引)
        int lastRet = -1; // index of last element returned; -1 if no such(最后要返回的元素索引,如果没有就为-1)
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }


三:使用lambda表达式便利Iterator

//JDK8 新增方法

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class demoMain {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("第一本书");
        list.add("第二本书");
        list.add("第三本书");
        Iterator<String> iterator = list.iterator();
        //方式一
        iterator.forEachRemaining(System.out::println);
        //方式二
        //iterator.forEachRemaining(s -> System.out.println(s));
    }
}

运行结果: 

四:使用 foreach 循环遍历集合元素

import java.util.ArrayList;
import java.util.List;

public class demoMain {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("第一本书");
        list.add("第二本书");
        list.add("第三本书");
        for (String s:list) {
            System.out.println(s);
        }
    }
}

运行结果: 

 

五:使用Stream遍历集合

import java.util.ArrayList;
import java.util.List;

public class demoMain {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("北京");
        list.add("上海");
        list.add("广州");
        list.add("深圳");

      //list.stream().forEach(s -> System.out.println(s));//方式一
        list.stream().forEach(System.out::println);//方式二
    }
}
import java.util.stream.Stream;

public class demoMain {
    public static void main(String[] args) {
        Stream stream=Stream.builder().add("第一本书").add("第二本书").add("第三本书").build();
        stream.forEach(System.out::println);
    }
}

运行结果: 

 

ArrayList源码分析:

ArrayLsit默认参数:

private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    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.
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;
     

    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

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);
        }
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    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 {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }

底层结构就是一个数组

ArrayList动态扩容:

 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        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);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
  1. 每添加一个元素,检查数组剩余空间是否足够
  2. 如果第一次添加元素,就创建容量为10的数组
  3. 如果数组剩余空间不足,触发扩容
  4. 每次扩容现有容量的50%
  5. 把旧数组内容拷贝到新数组
  6. 添加新元素

Arrays.copyOf &System.arraycopy

import java.util.Arrays;

public class demoMain {
    public static void main(String[] args) {
        /**
         * System.arraycopy  通过原数组的指定长度值改变目标数组中对应的值
         */
        String[] Array1 = {"a", "b", "c", "d"};
        String[] Array2 = {"1", "2", "3", "4"};
        System.arraycopy(Array1, 0, Array2, 0, 4);
        for (String s : Array1) {
            System.out.println("array1: " + s);
        }
        System.out.println("System.arraycopy后的结果:");
        for (String s : Array2) {
            System.out.println("array2: " + s);
        }
        /**
         *  Arrays.copyOf  只改变源数组的长度,对长度进行扩容
         */
        Object[] name = {"a", "b", "c", "d"};
        for (Object ojb : name) {
            System.out.println(ojb);
        }
        System.out.println("未改变之前的数组长度: " + name.length);
        name = Arrays.copyOf(name, 6);
        for (Object ojb : name) {
            System.out.println(ojb);
        }
        System.out.println("改变之后的数组长度: " + name.length);
    }
}
 System.arraycopy  只改变源数组的长度,对长度进行扩容
 
 Arrays.copyOf     只改变源数组的长度,对长度进行扩容

将第一个数组从第一位开始截取到长度为length=2的位复制到第二个数组0位开始

结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值