Collection:List集合

来源:互联网 发布:手机淘宝分享在哪 编辑:程序博客网 时间:2024/06/07 11:45

上一篇总结了一下Collection中的常用方法,它是所有集合的超类,那实现它的子类,肯定会有它特殊的方法,现在再了解一下它的其中一个集合List。

List也是一个接口,它又被好多子类实现,以他的常用的子类ArrayList来学习他的基本方法。

ArrayList底层是以数组来实现的。它的add方法可以看一看。下面是源码:

    public boolean add(E e) {        ensureCapacityInternal(size + 1);  // Increments modCount!!   初始化一个数组        elementData[size++] = e;<span style="white-space:pre"></span>//将对象e添加到第size+1个位置        return true;    }
    private void ensureCapacityInternal(int minCapacity) {        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {<span style="white-space:pre"></span>//如果集合是空的            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);      //创建一个数组,长度为10,默认ArrayList创建大小为10        }        ensureExplicitCapacity(minCapacity);    }
    private void ensureExplicitCapacity(int minCapacity) {        modCount++;        // overflow-conscious code        if (minCapacity - elementData.length > 0)            grow(minCapacity);    }
    private void grow(int minCapacity) {        // overflow-conscious code        int oldCapacity = elementData.length;        int newCapacity = oldCapacity + (oldCapacity >> 1);执行到这儿,就是说添加的数据长度超过了默认长度10,然后新的大小为10+10/2=15.下一次是15+15/2取整        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);     //重新初始化一个数组    }
在搜狐面试,问过的一个问题,现在还记忆犹新:ArrayList底层是怎么实现的,默认长度是多少?如果超出了范围,会如何?所以,在复习的时候,我特别认真的看了一下他的源码,所以,大概总结一下:ArrayList底层是以数组形式实现的,当添加一个元素时,相应的在数组中插入一条记录,数组的特点是查找速度快,但不方便删除和插入。如果要删除或插入一个元素,相应的后面都要向前移动一位,效率较低。每次初始化一个ArrayList时,默认创建一个大小为10的数组,当添加数量超过10条之后,继续初始化当前数组,将数组长度增加到[10/2](取整)即15,当长度再次超过时,将数组增加到15+[15/2]=22,以此类推。

上面是ArrayList的add方法。ArrayList在Collection的基础上,又添加了新的方法,常用的方法有:

E get(int index);       得到一个元素

E set(int index, E element); 更改指定位置的元素

void add(int index, E element); 向指定位置添加指定元素

E remove(int index); 移除指定位置的元素
int indexOf(Object o); 查找是否有该元素,返回出现的位置,如果不存在则返回-1

int lastIndexOf(Object o); 最后出现的位置

ListIterator<E> listIterator(); List的迭代器
List<E> subList(int fromIndex, int toIndex); 截取部分集合,返回截取后的集合

大部分都很好理解,其中indexOf(Object o)()和contains()很相似,前者返回查找后的位置,后者返回Boolean型。其中ListIterator<E> listIterator()比较特殊,它是一个List的迭代器,父类是Iterator,主要是在父类的基础上增加了一部分功能。下面先用一个具体的例子来看看他所加的功能。

public class ListTest {public static void main(String[] args) {List<Integer> list = new ArrayList<Integer>();list.add(1);list.add(2);list.add(3);ListIterator<Integer> listIterator = list.listIterator();//Iterator方法while(listIterator.hasNext()){Integer a = listIterator.next();System.out.println(a);if (a==2) {listIterator.add(4);<span style="white-space:pre"></span>//可以往集合中添加数据,添加位置在当前位置之后}}System.out.println("----------------------------");//ListIterator新增方法while(listIterator.hasPrevious()){//逆向遍历,返回是否有前一个元素System.out.println(listIterator.previous());//前一个元素}}}
输出结果:

123----------------------------3421
其实ListIterator的逆向遍历是没有太大意义的,如果不做正向遍历,逆向遍历不会输出任何数据。

最后的一个比较重要的方法是List<E> subList(int fromIndex, int toIndex)()。我个人觉得这个方法挺重要的,因为我前不久在研究彩票预测,其中要对集合中的数据进行截取,这个方法正好派上用场。主要是截取指定区间中的数据。用一个简单例子说明一下:

public class ListTest {public static void main(String[] args) {List<Integer> list = new ArrayList<Integer>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);List<Integer> subList = list.subList(1, 4);for (Integer integer : subList) {System.out.println(integer);}}}

输出结果 2 3 4.是从第i个位置开始到第j个位置,包含i但不包含j。

上面值得补充一点的是,indexOf(Object o)这个方法,如果我比较的是一个自定义对象,会是什么情况呢?下面做个测试,然后跟进源码进行分析一下:

public class Student {private String name;private Integer age;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Student(String name, Integer age) {super();this.name = name;this.age = age;}@Overridepublic String toString() {return "Student [name=" + name + ", age=" + age + "]";}}

public class ListTest {public static void main(String[] args) {List<Student> list = new ArrayList<Student>();Student stu1 = new Student("张三", 22);Student stu2 = new Student("李四", 27);Student stu3 = new Student("王五", 30);list.add(stu1);list.add(stu2);list.add(stu3);Student stu4 = new Student("李四", 27);System.out.println(list.indexOf(stu4));}}

上面这个结果最后输出的是-1,意思就是没有找到这个Student对象,我跟进一下源码去看看他的底层是如何实现的。

  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]))        /比较两个对象,这儿比较的是数组,如果要比较自定义对象,先重写equals方法                    return i;        }        return -1;    }

看到源码,主要起作用的是equals,而调用的equals方法是Object的,所以可以在自定义类中重写equals方法去实现这一功能。在Student中添加如下代码:

public boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Student other = (Student) obj;if (age != other.age)return false;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;return true;}

现在再次运行,就返回了具体的位置。1.


List集合中出了ArrayList之外,还有LinkedList、Vector等。他们都实现了List接口,里面大部分方法都是相同的,主要了解一下不同之处,再做个对比。

LinkedList:

ArrayList底层是以数组的形式去实现的,它的好处是方便查找,但是在删除和插入时,效率会很低。所以,为了更高效的去插入和删除时,就出现了LinkedList。LinkedList的底层是以链表的形式去实现的。链表,当前指针指向下一个节点,如果要插入一个数据,首先获取当前节点,将指针指向插入的新节点,再将插入的数据指向当前节点的下一个节点。举个例子:A-->B-->C-->D,现在要在B后面C的前面插入一个E,那么首先是得到B的指针,将B的指针指向E,然后再将E的指针指向C即可。这样插入,后面的元素不需要移动,因此效率是比较高的。LinkedList底层的插入就是以这种方式去实现的。

LinkedList特有的方法:

 public void addFirst(E e) 

public void addLast(E e)

因为LinkedList底层是链表形式去实现的,因此这两个方法就很容易理解了。

Vector:

Vector和ArrayList底层实现是一样的,他们的基本方法也是一模一样的,但是,ArrayList是线程不同步的,效率比较高,而Vector是线程同步的,所以效率比较低,一般现在都很少去使用Vector。

刚刚说到LinkedList适合插入和删除,而ArrayList适合查询,LinkedList插入和删除是线性的,因为它只是在指定位置加入或删除一个数据元素,其他元素不做相应的移动,而就插入和删除来说,ArrayList是二次增长的。因为它每次插入或者删除一个元素后,后面的元素都需要相应的向前面移动一个位置。同样查询也是如此。下面我用代码来说明一下它们之间的差距。

插入或删除:

public class ArrayListDemo {public static void main(String[] args) {List<Integer> list1 = new ArrayList<Integer>();//往list1中添加200000的值for (int i = 0; i < 200000; i++) {list1.add(i);}long startTime = System.currentTimeMillis();//获取开始时间Iterator<Integer> iterator = list1.iterator();while(iterator.hasNext()){//删除偶数项Integer next = iterator.next();if (next%2==0) {iterator.remove();}}long endTime = System.currentTimeMillis();//获取结束时间System.out.println(endTime-startTime);//运行时间}}
运行结果:1909毫秒

上面是ArrayList的删除时间,再看看LinkedList的删除时间

public class LinkedListDemo {public static void main(String[] args) {List<Integer> list1 = new LinkedList<Integer>();//往list1中添加200000的值for (int i = 0; i < 200000; i++) {list1.add(i);}long startTime = System.currentTimeMillis();//获取开始时间Iterator<Integer> iterator = list1.iterator();while(iterator.hasNext()){//删除偶数项Integer next = iterator.next();if (next%2==0) {iterator.remove();}}long endTime = System.currentTimeMillis();//获取结束时间System.out.println(endTime-startTime);//运行时间}}
运行结果:6毫秒。

由此可以看出效率之高。我把List值提高一倍,看看两个集合的时间,是不是一个是指数增长,一个是线性增长

ArrayList:8025毫秒,由此可知,它不是线性增长,当他的值增加到2倍后,所需要的时间,增加4倍。(由于计算机原因,时间有误差)

LinkedList:12毫秒,由此可知,它是线性增长。

同样,查询也是如此。







0 0
原创粉丝点击