作者:~小明学编程
文章专栏:Java数据结构
格言:目之所及皆为回忆,心之所想皆为过往
目录
常见方法
方法 | 解释 |
boolean add(E e) | 尾插 e |
void add(int index, E element) | 将 e 插入到 index 位置 |
boolean addAll(Collection<? extends E> c) | 尾插 c 中的元素 |
E remove(int index) | 删除 index 位置元素 |
boolean remove(Object o) | 删除遇到的第一个 o |
E get(int index) | 获取下标 index 位置元素 |
E set(int index, E element) | 将下标 index 位置元素设置为 element |
void clear() | 清空 |
boolean contains(Object o) | 判断 o 是否在线性表中 |
int indexOf(Object o) | 返回第一个 o 所在下标 |
int lastIndexOf(Object o) | 返回最后一个 o 的下标 |
List<E> subList(int fromIndex, int toIndex) | 截取部分 list |
public static void main(String[] args) {
ArrayList<String> lis = new ArrayList<>();
lis.add("hello");
lis.add("world");
lis.add("!");
lis.add("hello");
System.out.println(lis);//[hello, world, !, hello]
lis.add(2,"q");
System.out.println(lis);//[hello, world, q, !, hello]
lis.remove(1);
System.out.println(lis);//[hello, q, !, hello]
lis.remove("hello");
System.out.println(lis);//[q, !, hello]
}
难点分析
ArrayList(顺序表)
构造方法
方法 | 解释 |
ArrayList() | 无参构造 |
ArrayList(Collection<? extends E> c) | 利用其他 Collection 构建 ArrayList |
ArrayList(int initialCapacity) | 指定顺序表初始容量 |
如上表所示我们ArrayList()的构造方法一共有三种,第一种就是我们的无参构造,这种构造方法不用传参数直接new一个顺序表出来。
ArrayList<String> lis = new ArrayList<>();
第二种就是有参数的构造方法我们传一个已有的顺序表。
public static void main(String[] args) {
ArrayList<String> lis = new ArrayList<>();
lis.add("q");
lis.add("w");
ArrayList<String> lis1 = new ArrayList<>(lis);
System.out.println(lis1);//[q, w]
}
第三种则是传参传一个initialCapacity也就是给我们的顺序表一个初始容量。
ArrayList<String> lis = new ArrayList<>(10);
ArrayList的大小以及增容问题
ArrayList<String> lis = new ArrayList<>();
System.out.println(lis.size());//0
我们可以看到当我们无参构造的时候我们顺序表的大小居然是0,那么顺表的大小是0的话就肯定不能去放元素进去,所以这中间肯定会进行一个扩容的。我们通过查看源码并分析得出了这样的一个结论。
如果ArrayList调用不带参数的构造方法,那么顺序表的大小初始为0,当第一次add的时候,整个顺序表的大小将会变成10,之后每次放满在不超过最大增容限制的时候就会进行1.5倍的扩容,如果给定容量的大小了,那么初始值就是给定的大小(不超过最大限制),之后每次满了之后还是进行1.5倍的扩容。
打印顺序表
1.直接打印
System.out.println(lis);//toString方法打印
2.fori遍历
for (int i=0;i < lis.size();i++) {
System.out.println(lis.get(i));
}
通过我们的get()方法依次获取指定下标的元素进行打印。
3.forreach遍历
for (String ret: lis) {
System.out.println(ret);
}
4.迭代器打印
//迭代器打印
Iterator<String> it = lis.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
//迭代器list相关打印
ListIterator it2 = lis.listIterator();
while(it.hasNext()) {
System.out.println(it2.next());
}
public interface ListIterator<E> extends Iterator<E>
这是我们点开的源码我们可以看到 ListIterator<E> 继承了 Iterator<E>,所以ListIterator中将会有一些 Iterator中所没有的一些方法。
注意事项:
在我们使用Iterator的时候remove()之前必须得有next(),不能让迭代器在同一个位置remove()两次,否则的话将会抛出以下的异常。
Iterator<String> it = lis.iterator();
while(it.hasNext()) {
it.remove();
System.out.println(it.next());
}
Exception in thread "main" java.lang.IllegalStateException
所以我们想要删除集合中某一个元素的时候我们需要先next()迭代出当前的元素,然后再对其进行一个remove()。
正确的写法如下:
Iterator<String> it = lis.iterator();
while(it.hasNext()) {
String ret = it.next();
if (ret.equals("hello")) {
it.remove();
} else {
System.out.println(ret);
}
}
———————————————————————————————————————————
利用迭代器在读取的时候插入数据:
ListIterator<String> it2 = lis.listIterator();
System.out.println("============");
while(it2.hasNext()) {
String ret = it2.next();
if (ret.equals("hello")) {
it2.add("hahaha");
} else {
System.out.println(ret);
}
}
System.out.println("===============^");
System.out.println(lis);//[q, !, hello, hahaha]
值得注意的是我们的迭代器在读取的时候是可以it2.add,但是不能lis.add(),因为我们的ArrayList是单线程的属于线程不安全的,否则的话将会抛出异常。
参考源码实现顺序表(2.0版本)
class MyArrayList<E> {
private Object[] elementData;//数组
private int useSize;//有效的数据个数
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//默认的空数组
//不带参数的构造方法
public MyArrayList () {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//带参数的构造方法
public MyArrayList (int capacity) {
if (capacity>0) {
this.elementData = new Object[capacity];
} else if (capacity==0) {
this.elementData = new Object[0];
} else {
throw new IllegalArgumentException("初始容量小于0了");
}
}
public boolean add (E e) {
//确定一个真正的容量,判断一波是否需要扩容
ensureCapacityInternal(useSize + 1);
//现在可以放心的向里面放元素了
elementData[useSize++] = e;
return true;
}
public void add(int index,E e) {
rangeCheckForAdd(index);
ensureCapacityInternal(useSize+1);
copy(index,e);//拷贝数组
useSize++;
}
public int size() {
return useSize;
}
private void copy(int index,E e) {
for(int i=useSize-1;i>=index;i--) {
elementData[i+1] = elementData[i];
}
elementData[index] = e;
}
private void rangeCheckForAdd(int index) {
if (index > size() || index < 0)
throw new IndexOutOfBoundsException("index的位置不合法!");
}
private void ensureCapacityInternal(int minCapacity) {
//计算得出分析之后的容量
int capacity = calculateCapacity(elementData,minCapacity);
ensureExplicitCapacity(capacity);
}
private void ensureExplicitCapacity(int minCapacity) {
// 若分析之后的容量大于当前的容量则开始扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static int MAX_ARRAY_SIZE = Integer.MAX_VALUE-8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//确定新的容量的大小
int newCapacity = oldCapacity + (oldCapacity >> 1);//扩大1.5倍
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;
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//如果没有分配过大小
return Math.max(10,minCapacity);//返回一个10
}
return minCapacity;
}
}
List实战练习(扑克牌发牌器)
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
//扑克牌类
class Card {
private int rank;//数字
private String suit;//花色
public Card(int rank, String suit) {
this.rank = rank;
this.suit = suit;
}
@Override
public String toString() {
return "[ "+this.suit+" "+this.rank+" ]";
}
}
public class Test {
private static final String[] suits = {"♥","♠","♣","♦"};//花色数组
//买牌
public static List<Card> buyCard() {
//new一个顺序表用于存放我们的Card,这边给一个初始值因为我们已只牌的数量
List<Card> lis = new ArrayList<>(52);
for (int i = 0; i < 4; i++) {
for (int j = 1; j <= 13; j++) {
String suit = suits[i];
int rank = j;
Card card = new Card(rank,suit);
lis.add(card);
}
}
return lis;
}
//交换两个位置的牌
private static void swap(List<Card> cards,int i,int j) {
Card card = cards.get(i);
cards.set(i,cards.get(j));
cards.set(j,card);
}
//定义一个洗牌的方法
public static void shuffle(List<Card> cards) {
int size = cards.size();
for (int i = size-1;i>0;i--) {
Random random = new Random();
int rand = random.nextInt(i);
swap(cards,i,rand);
}
}
public static void main(String[] args) {
System.out.println("买牌中");
List<Card> cards = buyCard();
System.out.println("洗牌中");
shuffle(cards);
System.out.println("开始发牌");
ArrayList<List<Card>> hand = new ArrayList<>();
List<Card> hand1 = new ArrayList<>();
List<Card> hand2 = new ArrayList<>();
List<Card> hand3 = new ArrayList<>();
hand.add(hand1);
hand.add(hand2);;
hand.add(hand3);
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
hand.get(j).add(cards.remove(0));
}
}
System.out.println("第1个人的牌"+hand1);
System.out.println("第2个人的牌"+hand2);
System.out.println("第3个人的牌"+hand3);
System.out.println("剩余的牌"+cards);
}
public static void main1(String[] args) {
Card card = new Card(7,"♥");
System.out.println(card.toString());
}
}