2018.4.18
---| Collection 集合的总接口
------| List 接口 有序 可重复
---------| ArrayList [重点]
ArrayList 是底层维护了一个Object类型的数组,这样的话这个ArrayList既可以保持任意类型
的数据
特征:
当调用无参构造方法ArrayList,这里创建的底层Object类型的数组元素个数默认为10
DEFAULT-CAPACITY 10
查询快(有了下标基本确定找到元素),增删慢。
开发中使用ArrayList比较多的情景:
图书馆,人员管理
------| Set 接口 无序 不可重复
ArrayList特有方法:
//确定容量
ensureCapacity(int minCapacity); 不常用 返回值是boolean
判断当前ArrayList里面保存元素内容Object数组,元素个数是否大于minCapacity
//截断底层维护的Object类型的数组,让数组的容量变成当前ArrayList的size值【有效元素个数】
trimToSize(); 不太常用 eg:扩容时右移一位10W->15W,只增加一个元素。
Size();//拿出有效元素个数
【查询快,增删慢的原理】
查询快:
ArrayList底层维护的是一个Object类型的数组,可以完全使用数组的下标机制来访问数据,
这种访问形式是非常快的。
增加慢:
是因为在添加数据的时候,有可能导致ArrayList底层的Object的元素个数不会用,那么会调用数组
的扩容方法grow,而扩容方法是创建了一个新的数组,数组的元素个数大约是老数组的1.5倍,这里会
利用一些方法,把老数组里面的元素完完整整的拷贝到新数组,这个拷贝过程很占时间和内存。
删除慢:
因为删除某一个元素,会导致数组中该元素之后的数据,做一个整体的左移,这里也是一个数组的拷贝
过程,整个过程非常
面试题:
1.如果调用了ArrayList的无参构造方法,那么请问底层维护的Object数组默认的元素个数是多少?
如果是调用这个方法呢 new ArrayList(8);
答:默认元素个数为10,另一个为8.
2.ArrayList是一个可以自增长的空间,请问,增长的原理是什么?增长的长度是多少?
ArrayList底层维护的是一个Object数组,默认元素为10,如果添加元素时,当前需求的元素空间
超出了Object数组的元素个数,会调用底层的grow,进行数组元素的扩容和拷贝,扩容量是大约1.5倍
扩容是:
新元素个数 = 老元素个数 + (老元素个数 >> 1);
newCapacity = oldCapacity + (oldCapacity >> 1);
public class Demo1 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
}
}
HashCode 和 equals 判断对象是否相同。
contains, containsAll, equals方法
发现:
java语言中,默认判断两个对象是否相同的方式是,判断这两个对象的首地址是否相同,
在这里stu1和new Student(1,"梅西");是两个完全不同的对象,
问题:
但是Stu1和new Student(1,"梅西")里面保存的数据是一样的,也是符合业务逻辑
或者说生活逻辑的判断。
让判断符合语法的前提下也符合生活逻辑。
重写:
equals和hashCode方法。
默认情况下:
hashCode方法在系统默认的情况下,是当前类对象在内存地址的十进制数
equals方法是两个对象相互比较的法则。
class Student {
private int id;
private String name;
public Student() {}
public Student(int id,String name) {
this.id = id;
this.name = name;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
//描述当前类对象,当通过打印方法,打印该类对象的时候会自动调用。
public String toString() {
return "[ID:"+id+"name:"+name+"]";
}
/**
* equals和hashCode重写时一起重写。
*/
@Override
public boolean equals(Object obj) {
//这里equals方法是Student类重写的方法,当集合调用contains,containsAll和equals方法的时候
//都会来调用Student类里面的equals方法,进行比较,比较的的对象是Student对象。
System.out.println("equals方法");
//原本的比较方式是不符合生活逻辑的,只是判断两个对象的地址是否相同,不判断里面的内容是否一致
//期望,判断的是对象里面的数据是否一致。
//1.强制类型转换。
Student stu = (Student) obj;
System.out.println(this.name+"和"+stu.name+"进行比较");
//this.name.equals(stu.name);这里的equals方法是调用的String类型的equals方法,和重写的equals没关系。
return this.id == stu.id && this.name.equals(stu.name);
}
@Override
public int hashCode() {
System.out.println("hashCode方法");
//重写了equals方法,同时也要重写hashCode方法。
//hashCode值要确定【唯一性】,只要满足你自己的逻辑就可以了。
//这里认为Id是唯一的。
return this.id;
}
}
public class Demo1 {
public static void main(String[] args) {
Collection c = new ArrayList();
Student stu1 = new Student(1,"梅西");
Student stu2 = new Student(2,"C罗");
Student stu3 = new Student(3,"内马尔");
c.add(stu1);
c.add(stu2);
c.add(stu3);
boolean ret = c.contains(new Student(1,"伊布"));
System.out.println(c);
System.out.println("ret:"+ret);
}
}
结果:
equals方法
伊布和梅西进行比较
equals方法
伊布和C罗进行比较
equals方法
伊布和内马尔进行比较
[[ID:1name:梅西], [ID:2name:C罗], [ID:3name:内马尔]]
ret:false
iterator迭代器
iterator();迭代器
循环,作用是循环遍历整个集合,
public class Demo2 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("栈桥");
c.add("八大关");
c.add("情人坝");
c.add("中山公园");
//第一种遍历方式:将集合转换成数组进行遍历
/*Object[] array = c.toArray();
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
*/
/*
LOW
会导致内存资源的浪费,会拷贝一份完整的集合数据,如果集合数据过大,甚至于会超过内存的最大致
Iterator 迭代器的方法
boolean hasNext();判断当前迭代器是否有下一个元素
Object next();获取当前迭代器指向的元素,并且获取之后,指向下一个元素。 object为迭代器对象。
void remove();删除当前迭代器通过的next获取到的对象。
[要求]:
在通过迭代器调用remove方法的时候,之前必须调用过next()方法。
否则报异常: java.lang.IllegalStateException
*/
Iterator it = c.iterator();//返回当前集合的一个迭代器
/*
System.out.println("当前元素中有没有下一个元素:"+it.hasNext());
System.out.println("当前迭代器指向的元素:"+it.next());
System.out.println("当前迭代器指向的元素:"+it.next());
System.out.println("调用了删除的方法");
it.remove();
*/
//利用迭代器,借助于hasNext和next方法,完成对整个集合的遍历。
while(it.hasNext()) {
System.out.println("迭代器操作:"+it.next());
//it.remove();这里可以调用remove方法,用来清空集合。
//it.next();(获取当前元素,并指向下一个元素)
}
System.out.println(c);
}
}
结果:
迭代器操作:栈桥
迭代器操作:八大关
迭代器操作:情人坝
迭代器操作:中山公园
[栈桥, 八大关, 情人坝, 中山公园]
LinkedList
LinkedList 底层维护的是一个链表。
特征:
增删快,查找慢。
LinkedList特有方法:
addFirst(Object o);
addLast(Object o)
getFirst();
getLast();
removeFist();
removeLast();
*/
public class Demo1 {
public static void main(String[] args) {
LinkedList list = new LinkedList();
}
}
List接口
List接口中【特有的方法】:
添加:
add(index,Object o);在指定位置上放入元素 addAll(int index, Collection c);在指定位置上添加一个集合
获取:
object get(int index);获取指定下标的元素 obejct---list int indexOf(Object o);获取某个元素的下表位置 int lastIndex(Object o);找出指定元素最后一次出现在集合中的位置。(在元素相同时) List sublist(int fromIndex,int toIndex);获取一个子List集合
修改:
set(int index, Object o);设置指定下标上的元素 get(int in)
迭代:
ListItrator(); public class Demo1 { public static void main(String[] args) { List list = new ArrayList(); list.add("鱼辣子"); list.add("沙蟹汁"); list.add("鲱鱼罐头"); list.add("活蛆奶酪"); System.out.println(list); list.add(1,"变蛋"); System.out.println(list); List list2 = new ArrayList(); list2.add("麻辣小龙虾"); list2.add("湖南红烧肉"); list2.add("麻辣小龙虾"); list.addAll(2,list2); System.out.println(list); System.out.println(list.get(5)); System.out.println(list.indexOf("沙蟹汁")); System.out.println(list.lastIndexOf("麻辣小龙虾")); //subList(int fromIndex,int toIndex);
在JAVA中所有用到区间范围的操作 都是要头不要尾。
List subList = list.subList(0, 5);
System.out.println(subList);
list.set(list.indexOf("鲱鱼罐头"),"长沙臭豆腐");
System.out.println(list);
结果:
[鱼辣子, 沙蟹汁, 鲱鱼罐头, 活蛆奶酪]
[鱼辣子, 变蛋, 沙蟹汁, 鲱鱼罐头, 活蛆奶酪]
[鱼辣子, 变蛋, 麻辣小龙虾, 湖南红烧肉, 麻辣小龙虾, 沙蟹汁, 鲱鱼罐头, 活蛆奶酪]
沙蟹汁
5
4
[鱼辣子, 变蛋, 麻辣小龙虾, 湖南红烧肉, 麻辣小龙虾]
[鱼辣子, 变蛋, 麻辣小龙虾, 湖南红烧肉, 麻辣小龙虾, 沙蟹汁, 长沙臭豆腐, 活蛆奶酪]