今天我们学习的是ArrayList。
目录
ArraysList底层是一个顺序表,既然是顺序表那就是一个数组。我们可以看到,ArrayList实现了非常丰富的接口类型。我们接下来开始跟着源码学习。
1.构造方法
ArrayList() : 这是一个无参构造
public class Test { public static void main(String[] args) { ArrayList<Integer> arrayList1 = new ArrayList<>(); arrayList1.add(1); arrayList1.add(2); System.out.println(arrayList1); }
ArrayList(Collection<? extends E> c)
public class Test { public static void main(String[] args) { ArrayList<Integer> arrayList1 = new ArrayList<>(); arrayList1.add(1); arrayList1.add(2); System.out.println(arrayList1); ArrayList<Integer> arrayList2 = new ArrayList<>(arrayList1); }
第二个构造方法里面,最后一句代码的意思是把arrayList1里面的元素放到arrayList2里面去。并且只要是Collection里面的方法都是可以的。
ArrayList(int initialCapacity)
这个构造方法说的是自定义数组的长度
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); } }
2.ArrayList的数组
默认容量为10
elementData相当于就是顺序表背后的那个数组
有效的数据个数
但是这个构造方法,它并没有分配数组的内存
也就是说elementData既然是没有大小的,那么怎么把数据放进去呢?
这个时候我们点到add方法里面去:
public boolean add(E e) {
//必须放一个E类型的数据
ensureCapacityInternal(size + 1);
elementData[size++] = e;//这就是把数据放到最后一个元素的后面
return true;
}
我们再点到ensureCapacityInternal方法里面去,此时没有元素,那么size就是0。
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
minCapacity此时为1,我们再点到calculateCapacity方法里面去:
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
此时elementData为0,进入if条件句很明显有一个max方法赋值,那么最终返回的是DEFAULT_CAPACITY也就是10,那么把10作为 ensureExplicitCapacity的参数:
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
再点到grow函数中:
private void grow(int minCapacity) {
int oldCapacity = elementData.length;//此时为0
int newCapacity = oldCapacity + (oldCapacity >> 1);//仍然为0
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; //此时对newCapacity赋值为10
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);那么到此才是给elementData赋值
}
那既然现在大小为10,放第十一个元素的时候怎么扩容呢?在grow的第三行代码已经是采用了1.5倍的扩容。
也就是说第一个构造方法并不会给数组分配空间,但是第一次添加元素的时候会调用add方法此时再经过一系列的函数方法最终把数组初始化为10的大小。但是如果是第三种构造方法可以直接初始化数组的大小。
3.ArrayList的常见操作
对于remove,先看E remove(int index),输入的index数据默认为删除的是index位置的元素,要使删除的是想要删除的数据,需要输入为 ArrayList.remove(new Integer(7));
4.数组的遍历
1.for循环
for (int i = 0 ; i < arrayList1.size();i++){ System.out.print(arrayList1.get(i)+" "); }
2.for each遍历
for (Integer x : arrayList1 ) { System.out.print(x+" "); }
冒号的左边写类型和名字,右边写数组名
3.迭代器遍历
Iterator<Integer> it = arrayList1.iterator(); while(it.hasNext()){ System.out.println(it.next()+" "); System.out.println(""); }
迭代器基本使用方法如此,运用Iterator,每次读取的就是next的值,就可以一直打印下去
5.数组的扩容
ArrayList是一个动态类型的顺序表,即:在插入元素的过程中会自动扩容
1. 检测是否真正需要扩容,如果是调用grow准备扩容
2. 预估需要库容的大小 初步预估按照1.5倍大小扩容 如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容 真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
3. 使用copyOf进行扩容
6.Collections.sort的排序
public class TestDemo {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList<>();
arrayList.add(new student("张三",19,88.5));
arrayList.add(new student("李四",23,89.5));
arrayList.add(new student("王五",20,85.5));
for(int i = 0 ; i < arrayList.size();i++){
System.out.println(arrayList.get(i) + " ");
}
Collections.sort(arrayList);
}
}
我们在运用ArrayList输出一个学生的姓名、年龄、分数的时候,对于分数的排序我们可以用到Collections.sort来进行排序。但是如果直接把arraylist放到括号里面会发现编译器报错了。
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
我们发现这个类型extends Comparable,所以我们需要在class student后面写上:
class student implements Comparable<student>{}
同时我们还需要重新toString方法:
@Override
public int compareTo(student o) {
return (int)(this.score - o.score);
}
这样就可以以分数为序列进行排序了。
7.题目:删除1号字符串中的2号字符串
例如:1:welcome to China 2:come
输出:wl t hina
具体思路就是用ch.charAt接收每一个str1里面的字符,然后与str2对比。若没有对比成功,那么就在arrayList中加上这个字符。但是发现str2.contains(ch)报错了。这时只需要在ch后面加上一个“ ”就能解决:
public static void func(String str1,String str2){
ArrayList<Character> arrayList = new ArrayList<>();
for (int i = 0; i < str1.length(); i++) {
char ch = str1.charAt(i);
if(!str2.contains(ch+"")){
arrayList.add(ch);
}
}
}
string类型加上一个“ ”就变成了一个字符串类型,这是一个小技巧。
8.扑克牌的实现
先新建一个poker类,定义好相关的基础信息以及构造方法等等
public class Poker {
public String suit;//花色
public int rank;//点数
public Poker(String suit, int rank) {
this.suit = suit;
this.rank = rank;
}
public String getSuit() {
return suit;
}
public void setSuit(String suit) {
this.suit = suit;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
@Override
public String toString() {
return suit+" "+rank;//重写toString方法
}
}
然后再建一个pokers类,写出花色和最开始的方法:buyPokers:
public static List<Poker> buyPokers(){
List<Poker> pokerList = new ArrayList<>();//我们在此新建一个ArrayList
for (int i = 0; i < 4; i++) { //i的取值为四种花色
for (int j = 0; j < 13; j++) { //j的取值为13个不同的大小
String suit = SUITS[i]; //suit就是新建的花色
Poker poker = new Poker(suit,j); //此时poker已经是一张牌了
pokerList.add(poker); //把一张poker添加到pokerlist里面去
}
}
return pokerList; //最后再返回
}
接着我们需要洗牌,用到的方法是shuffle和swap:
public static void shuffle(List<Poker> pokerList//指的是传进来的是这种类型的这个变量) {
Random random = new Random();
for (int i = pokerList.size()-1; i > 0 ; i--) { //i初始比数组大小小1,再逐个递减
int index = random.nextInt(i); //定义index就是i的随机值
swap(pokerList,i,index); //此时再传入swap函数里去
}
}
public static void swap(List<Poker> pokerList,int i,int j){
Poker tmp = pokerList.get(i); //定义tmp为第三交换变量
pokerList.set(i,pokerList.get(j)); //get(j)的元素和i交换
pokerList.set(j,tmp); //tmp和j交换
}
最后就是3个人揭牌,每个人揭5张牌:
public static void main(String[] args) {
List<Poker> pokerList = buyPokers();
System.out.println("买牌"+ pokerList);
shuffle(pokerList);
System.out.println("洗牌"+ pokerList);
List<Poker> hand1 = new ArrayList<>(); //这里的hand1,2,3就代表着三个人
List<Poker> hand2 = new ArrayList<>();
List<Poker> hand3 = new ArrayList<>();
List<List<Poker>> hand = new ArrayList<>();//注意,这里的List<List<Poker>>代表的是在这个List里面,每一个元素都是List类型,也就是说这是一个有元素类型是List,大小是3的List
hand.add(hand1); //接口里面的add方法,把hand1,2,3添加到hand里面去
hand.add(hand2);
hand.add(hand3);
for (int i = 0; i < 5; i++) { //i就是5张牌
for (int j = 0; j < 3; j++) { //j就是三个不同的人
List<Poker> handtmp = hand.get(j); //get(j)就是拿到不同的手,代表着
不同的人
handtmp.add(pokerList.remove(0)); //handtmp就是拿到的就是不同的人,
添加进去就是这个人的牌
} //pokerList.remove(0) 因为这个永远代表着数组的第一个元素,所以正好添加进去的元素和数组的元素拿出来可以共用一条语句
}
for (int i = 0; i < hand.size(); i++) {
System.out.println("第"+(i+1)+"个人的牌是"+hand.get(i));
}
System.out.println("剩余的牌是:"+pokerList);
}
ArrayList有一定难度,但是这才只是数据结构的开始。
革命尚未成功,同志仍需努力。