前言:这周主要就是对集合的学习
集合
什么是集合
1、 集合是Java API所提供的一系列类,可以用于动态存放多个对象。--
集合只能存对象
2、 集合与数组的不同在于,集合是大小可变的序列,而且元素类型可以不受限定,只要是引用类型。(集合中不能放基本数据类型,但可以放基本数据类型的包装类)
3、 集合类可以自动扩容。
4、 集合类全部支持泛型,是一种数据安全的用法。
集合与数组的区别
数组和集合都是容器
数组是定义完成并运行后,类型确定、长度固定。数组适合元素的个数和类型确定的业务场景,不适合做需要增删数据操作.
集合是大小不固定,运行后可以动态变化,类型也可以选择不固定。集合非常适合做元素的增删操作。
集合框架总体结构
Java中集合类定义主要是java.util.*包下面,常用的集合在系统中定义了三大接口,这三类的区别是:
java.util.Set接口及其子类,set提供的是一个无序的集合;
java.util.List接口及其子类,List提供的是一个有序的集合;
java.util.Map接口及其子类,Map提供了一个映射(对应)关系的集合数据结构;
另外,在JDK5中新增了Queue(队列)接口及其子类,提供了基于队列的集合体系。每种集合,都可以理解为用来在内存中存放一组对象的某种”容器“---就像数组,就像前面我们自己定义的队列。
ArrayList
ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作。
ArrayList接口特有的方法:
List集合因为支持索引,所以多了很多索引操作的独特api,其他Collection的功能List也都继承了。
方法名称 | 说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
手动实现底层源码
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class MyArrayList<E> implements Iterable<E> {
private Object[] elementData = {};
private int size = 0;//数组实际长度指针
//初始化数组
public MyArrayList() {
elementData = new Object[10];
}
//有参构造
public MyArrayList(int initialCapacity) {
if (initialCapacity < 0) {
throw new IndexOutOfBoundsException("无效初值");
}
elementData = new Object[initialCapacity];
}
//数组扩容
private void ensureCapacity(){
elementData = Arrays.copyOf(elementData, elementData.length * 2);
}
//添加元素
public void add(E element){
//判断是否需要扩容
if (size+1 > elementData.length) {
ensureCapacity();
}
elementData[size++] = element;
}
//根据索引插入元素
public void add(int index,E element){
//异常处理
if (index > size || index < 0) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
//判断是否需要扩容
if (size+1 > elementData.length) {
ensureCapacity();
}
//循环移动元素
for (int i = size; i > index; i--) {
elementData[i] = elementData[i-1];
}
elementData[index] = element;
size++;
}
//根据索引删除元素,并返回
public E remove(int index){
if (index > size || index < 0) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
E elementDatum = (E) elementData[index];
for (int i = index; i < size-1; i++) {
elementData[i] = elementData[i+1];
}
elementData[size - 1] = null;
size--;
return elementDatum;
}
//判断是否含有该元素
public boolean contains(E element){
for (int i = 0; i < size; i++) {
if (elementData[i].equals(element)) {
return true;
}
}
return false;
}
//返回数组中实际长度
public int size(){
return size;
}
//返回索引处元素
public E get(int index){
if (index >= size || index < 0) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
return (E) elementData[index];
}
//判空
public boolean isEmpty(){
return size == 0;
}
//修改索引处的元素值,并返回原先的值
public E set(int index,E element){
if (index >= size || index < 0) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
E elementDatum = (E) elementData[index];
elementData[index] = element;
return element;
}
//重写toString方法
@Override
public String toString() {
StringBuffer sb = new StringBuffer("[");
for (int i = 0; i < size; i++) {
sb.append(elementData[i]);
if (i < size-1) {
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
public void clear(){
for (int i = 0; i < size; i++) {
elementData[i] = null;
}
size = 0;
}
//迭代器
@Override
public Iterator<E> iterator() {
return new MyListIterator();
}
private class MyListIterator<E> implements Iterator<E>{
private int currentIndex = 0;
@Override
public boolean hasNext() {
return currentIndex < size;
}
@Override
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return (E) elementData[currentIndex++];
}
}
}
LinkedList
底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API。
LinkedList集合的特有功能:
方法名称 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
手动实现底层源码
import java.util.NoSuchElementException;
public class MyLinkedList<E> {
private int size;
private Node<E> first = null;//头
private Node<E> last =null ;//尾
//初始化
public MyLinkedList() {
this.first = null;
this.last = null;
this.size = 0;
}
//返回尾节点
public E getFirst() {
if (first == null) {
throw new NoSuchElementException("LinkedList is empty");
}
return first.value;
}
//返回头节点
public E getLast() {
if (last == null) {
throw new NoSuchElementException("LinkedList is empty");
}
return last.value;
}
//删除尾节点
public E removeFirst(){
if (first == null) {
throw new NoSuchElementException("LinkedList is empty");
}
E value = first.value;
first = first.next;
if (first != null ) {
first.prev = null;
}else {
last = null;
}
size--;
return value;
}
//删除头节点
public E removeLast(){
if (last == null) {
throw new NoSuchElementException("LinkedList is empty");
}
E value = last.value;
last = last.prev;
if (last != null) {
last.next = null;
}else {
first = null;
}
size--;
return value;
}
// 判断链表是否为空
public boolean isEmpty() {
return size == 0;
}
//添加方法,默认尾插法
public void add(E e){
Node<E> newNode = new Node<>(last,e,null);
if (isEmpty()) {
first = newNode;
}else {
last.next = newNode;
}
last = newNode;
size++;
}
//在该列表开头插入指定的元素
public void addFirst(E e){
Node<E> newNode = new Node<>(null, e, first);
if (isEmpty()) {
last = newNode;
}else {
first.prev = newNode;
}
first = newNode;
size++;
}
//将指定的元素追加到此列表的末尾
public void addLast(E e){
Node<E> newNode = new Node<>(last, e, null);
if (isEmpty()) {
first = newNode;
}else {
last.next = newNode;
}
last = newNode;
size++;
}
//索引位置添加
public void add(int index,E e){
if (index > size || index < 0) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
if (index == size) {
add(e);
return;
}
Node<E> newNode = new Node<>(null, e, null);
if (index == 0) {
newNode.next = first;
first.prev = newNode;
first = newNode;
}else {
Node<E> temp = first;
for (int i = 0; i < index-1; i++) {
temp = temp.next;
}
newNode.next = temp.next;
newNode.prev = temp;
temp.next.prev = newNode;
temp.next = newNode;
}
size++;
}
//链表长度
public int size(){
return size;
}
//通过索引返回元素值
public E get(int index){
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
Node<E> temp = first;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
return temp.value;
}
//修改索引处的值,返回原先的值
public E set(int index,E e){
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
Node<E> temp = first;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
E value = temp.value;
temp.value = e;
return value;
}
//判断链表是否含有此元素
public boolean contains(E e) {
Node<E> current = first;
while (current != null){
if (current.value.equals(e)) {
return true;
}
current = current.next;
}
return false;
}
//删除索引节点,返回其值
public E remove(int index){
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
if (index == 0) {
return removeFirst();
}else if (index == size-1) {
return removeLast();
}else {
Node<E> temp = first;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
E value = temp.value;
temp.next.prev = temp.prev;
temp.prev.next = temp.next;
size--;
return value;
}
}
//清空链表
public void clear(){
first = null;
last = null;
size = 0;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("[");
Node<E> temp = first;
while (temp != null){
sb.append(temp.value);
if (temp.next != null) {
sb.append(", ");
}
temp = temp.next;
}
sb.append("]");
return sb.toString();
}
private static class Node<E> {
E value;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.value = element;
this.next = next;
this.prev = prev;
}
}
}
ArrayList和LinkedList的区别
ArrayList | LinkedList |
---|---|
底层封装数组实现,分配的是一块连续的内存空间 | 底层封装链表实现,分配的是不连续的内存空间 |
读取数据效率更高 | 读取数据效率相对较低 |
增,删等操作效率相对偏低 | 增,删等操作效率高 |
相对于ArrayList,多了对于首元素和尾元素的操作 |