java集合讲解以及主要的LinkedList和ArrayList《Lipp学习笔记》

集合自学笔记

time:2022/02/24

总概括

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3lStNdp6-1645688472649)(./images/1.gif)]
所有东西都是继承于Iterator
集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。
Collection接口有三个子接口:List,Set,Queue
常见集合接口:

  • Collection
    Collection接口储存一组不唯一,无序对象

  • List
    一个有序的Collection,能类似数组通过索引来访问List元素,第一个元素索引为0,而且允许有相同元素。

  • Set
    接口只是用来继承Collection,因为Collection不可直接继承(个人理解),

  • sortedSet
    继承于Set用来报讯有序集合

  • Map
    用来存储键值对元素,提供Key到Value的映射

  • SortedMap
    继承于 Map,使 Key 保持在升序排列

List和Set的区别

  • set是无序不重复数据,List存储有序可重复数据
  • set检索效率低,增删效率高,List与其相反
  • List用法与数组类似,可动态增长。

集合实现类(包括抽象类)

  • AbstractCollection
    实现大部分collection接口
  • AbstractList
    继承AbstractCollection,并实现了大部分List接口
  • ArrayList
    继承顺序:ArrayList—>List—>ListCollection
ArrayList的使用
//默认创建一个ArrayList集合
List<String> list = new ArrayList<>();
//创建一个初始化长度为100的ArrayList集合
List<String> initlist = new ArrayList<>(100);
ArrayList源码
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

    private static final int DEFAULT_CAPACITY = 10;

    private static final Object[] EMPTY_ELEMENTDATA = {};

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    transient Object[] elementData; 
   
    private int size;

    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) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
}

从构造方法ArrayList(),可以看出ArrayList底层是Object类,将添加的数据保存到elemenData中


待更新,等笔者能力上升再补充


ArrayList常用方法
  1. add()
    源码
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);
    // Increnter code     hereements modCount!!
        elementData[size++] = e;
        return true;
}

ensureCapacityInternal()

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

ensureExplicitCapacity()

private void ensureExplicitCapacity(int minCapacity) {
	modCount++;
	// overflow-conscious code
	if (minCapacity - elementData.length > 0)
		grow(minCapacity);
}

grow()进行数组扩容,
扩容规则就是数组原满足最小容量+数组原满足最小容量的一半
>>作用:
将二进制数据右移动一位,相当于减少一半

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

addall()方法添加是一个list

/Collection c内的数据插入ArrayListpublic boolean addAll(Collection<? extends E> c) {undefined
        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;
    }

    //将Collection c中的数据插入到ArrayList的指定位置
    public boolean addAll(int index, Collection<? extends E> c) {undefined
        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;
    }


后面学习再补充addall()方法


  1. set()方法
    源码
public E set(int index, E element) {
    rangeCheck(index);
    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

EXample

public class BRRead {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>(10);
		list.add("牛魔王");
		list.add("蛟魔王");
		list.add("狮驼王");
		list.add("鹏魔王");
		list.add("美魔王");
		list.add("猕魔王");
		list.add("猪魔王");
		System.out.println(list.size());
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
	}
}

console打印结果

7
牛魔王
蛟魔王
狮驼王
鹏魔王
美魔王
猕魔王
猪魔王

添加set方法后

for (int i = 0; i < list.size(); i++) {
			if(i==5) {
				list.set(i, "孙悟空");
			}
			System.out.println(list.get(i));
			
		}

效果

7
牛魔王
蛟魔王
狮驼王
鹏魔王
美魔王
孙悟空
猪魔王
  1. get()方法

已经用过了

  1. remove(int index)
    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;
}
  1. subList()
    源码
public List<E> subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
}
private class SubList extends AbstractList<E> implements RandomAccess {
    private final AbstractList<E> parent;
    private final int parentOffset;
    //用于保存偏移量
    private final int offset;
    int size;

    SubList(AbstractList<E> parent, int offset, int fromIndex, int toIndex) {
        this.parent = parent;
        this.parentOffset = fromIndex;
        this.offset = offset + fromIndex;
        this.size = toIndex - fromIndex;
        this.modCount = ArrayList.this.modCount;
    }
}
    1.sublist()方法返回的是一个元素视图
    2.对于视图的任何操作都会被原集合取代

    3.add()方法

源码

    public boolean add(E e) {
    add(size(), e);
    return true;
}

public void add(int index, E e) {
    rangeCheckForAdd(index);
    checkForComodification();
    parent.add(parentOffset + index, e);
    this.modCount = parent.modCount;
    this.size++;
}

还有很多方法,后面再补充。。。。。


  • LinkedList

  1. linkedList的定义
    例子:
List stringList = new LinkedList<>();
		List tempList = new ArrayList<>();
//		tempList.add("牛魔王");
//		tempList.add("蛟魔王");
//		tempList.add("鹏魔王");
//		tempList.add("狮驼王");
//		tempList.add("猕猴王");
//		tempList.add("禺贼王");
//		tempList.add("美猴王");
		List stringList2 = new LinkedList<>(tempList);
		System.out.println(stringList);
		System.out.println("分界线::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
		System.out.println(stringList2);

结果:

[]
分界线::::::::::::::::::::::::::::::::::::::::::::::::::::::::
[]

由此可见,在我们定义linkedList时候,可以定义一个空集合或者传递一个构造好的集合。
源码:

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
    transient int size = 0;

    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;

    /**
     * Constructs an empty list.
     */
    public LinkedList() {
    }

    /**
     * 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 LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }
}
  1. 三个变量
    size:集合的长度
    first:双向链表头部节点
    last:双向链表尾部节点

  2. linkedList是通过双向链表实现,而双向链表通过Node这个静态内部类实现。

private static class Node {
    E item;
    Node next;
    Node prev;
    Node(Node prev, E element, Node next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}
    

2.LinkedList常用方法
get()
源码:

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}

/**
 * 返回一个指定索引的非空节点.
 */
Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

分析源码可知,get()是通过node()方法来实现,他的本质就是,有两个参数,一个index–需要获取的node下标,一个size–LinkedList的大小,通过比较index与size*1/2的大小,index小于size/2,就从前遍历寻找,反之,从尾部遍历寻找
2.add()方法
下面为add(E e)

public boolean add(E e) {
    linkLast(e);
    return true;
}

/**
 * 设置元素e为最后一个元素
*/
void linkLast(E e) {
    final Node l = last;
    final Node newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}
分析代码可得,这个方法默认添加元素在LinkedList链表尾部。

源码2:

public void add(int index, E element) {
    checkPositionIndex(index);
    if (index == size)
        linkLast(element);
    else
        linkBefore(element, node(index));
}

/**
 * 在一个非空节点前插入一个元素
 */
void linkBefore(E e, Node succ) {
    // assert succ != null;
    final Node pred = succ.prev;
    final Node newNode = new Node<>(pred, e, succ);
    succ.prev = newNode;
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}

很明显,在实际开发中,查询功能使用比较频繁,如果有增删的需求那就定义LinkedList,如果只是query业务是主要,那就定义ArrayList,因为随机存取的算法复杂度为O(1)
3。remove()
源码:

//删除某个对象
public boolean remove(Object o) {
    if (o == null) {
        for (Node x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}
//删除某个位置的元素
public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}
//删除某节点,并将该节点的上一个节点(如果有)和下一个节点(如果有)关联起来
E unlink(Node x) {
    final E element = x.item;
    final Node next = x.next;
    final Node prev = x.prev;

    if (prev == null) {
        first = next;
    } else {
        prev.next = next;
        x.prev = null;
    }

    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }

    x.item = null;
    size--;
    modCount++;
    return element;
}

由于我学过数据结构,我就不记录了
反正就是指向next
4.LinkedList遍历
源码:

public class Test {

	public static void main(String[] args) {
	    LinkedList list = getLinkedList();
	    //通过快速随机访问遍历LinkedList
	    listByNormalFor(list);
	    //通过增强for循环遍历LinkedList
	    listByStrengThenFor(list);
	    //通过快迭代器遍历LinkedList
	    listByIterator(list);
	}

	/**
	 * 构建一个LinkedList集合,包含元素50000个
	 * @return
	 */
	private static LinkedList getLinkedList() {
	    LinkedList list = new LinkedList();
	    for (int i = 0; i < 50000; i++){
	        list.add(i);
	    }
	    return list;
	}

	/**
	 * 通过快速随机访问遍历LinkedList
	 */
	private static void listByNormalFor(LinkedList list) {
	    // 记录开始时间
	    long start = System.currentTimeMillis();
	    int size = list.size();
	    for (int i = 0; i < size; i++) {
	        list.get(i);
	    }
	    // 记录用时
	    long interval = System.currentTimeMillis() - start;
	    System.out.println("listByNormalFor:" + interval + " ms");
	}

	/**
	 * 通过增强for循环遍历LinkedList
	 * @param list
	 */
	public static void listByStrengThenFor(LinkedList list){
	    // 记录开始时间
	    long start = System.currentTimeMillis();
	    for (Integer i : list) { }
	    // 记录用时
	    long interval = System.currentTimeMillis() - start;
	    System.out.println("listByStrengThenFor:" + interval + " ms");
	}

	/**
	 * 通过快迭代器遍历LinkedList
	 */
	private static void listByIterator(LinkedList list) {
	    // 记录开始时间
	    long start = System.currentTimeMillis();
	    for(Iterator iter = list.iterator(); iter.hasNext();) {
	        iter.next();
	        
	    }
	    // 记录用时
	    long interval = System.currentTimeMillis() - start;
	    System.out.println("listByIterator:" + interval + " ms");
	}
}

结果:

listByNormalFor:767 ms
listByStrengThenFor:3 ms
listByIterator:2 ms

增强for循环本质就是调用迭代器Iterator,只是多了一个赋值。

你可能感兴趣的:(java,学习,集合,arraylist,linked,list)