集合
1.定义
1.集合可以看作是一种容器,用来存储对象信息的容器,并且长度是可变的。
-
因为对象是可以创建无限个数的,而对象数组是开辟固定长度的存储空间,显然用对象是不合适的
-
对象数组长度不可变化并且无法保存具有映射关系的数据;集合可以用于存储数量不确定的数据,长度可变,以及可以保存具有映射关系的数据
-
数组元素既可以是基本类型的值,也可以是对象;集合只能保存对象(比如list集合add(1)方法,是把1变成包装类Integer包装类)
- 所有的集合继承关系图(虽然很全,但是我们不是都要掌握,我们掌握工作中常用到的就可以),掌握这个文档的就足够你跟面试官吹逼了。
- 如果觉得上面太复杂,看这个简化版如图:
- 常用集合图
2.集合的使用
2.1 list集合
-
list接口集合代表一个有序、可重复的集合,集合每个元素都有其对应的顺序索引。list集合默认按照元素的添加顺序设置元素的索引,可以通过索引来访问指定位置的集合元素。
-
实现list接口的集合常用的有:ArrayList、linkedList、vector子类等,实现list接口有哪些子类具体参考打开层级目录去看,不需要使用都学会。
2.1.1ArrayList
-
ArrayList底层是一个object类型的数组,也是我们最常用于数据遍历的集合.它允许任何符合规则的数据插入设置是null。该object类型的数组集合的默认长度为0,也可以自定义长度。每次所以list.add()方法插入元素时,对容器进行扩容。
-
如图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kv2k1oRi-1599571925845)(D:\downfile\QQ\tu1.png)]
-
list.get(i):直接获取数组的指定下标位置的元素,查询快。
@SuppressWarnings("unchecked") E elementData(int index) { return (E) elementData[index]; }
-
ArrayList是一个有序可扩容长度的数组,所以可以插入有序的可重复的元素.优点是:可以快速通过下标获取到元素,遍历元素和查找元素快;由于插入、删除需要位移,所以插入、删除效率慢。
-
同时ArrayList是非同步的,是非线程安全的,效率高。
-
案例1:
import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { //底层是一个数组,每次插入值的时候判断有没有位置并且扩容保证数组能够无限的插入值 List lists = new ArrayList(); lists.add(100); lists.add("马云"); lists.add(1000.98); lists.add(1,"王健林"); lists.add(1,100); System.out.println("集合长度:" + lists.size()); System.out.println("是否包含:"+(lists.contains(100)?"有100":"没有100")); System.out.println(lists.get(1)); // list.clear();//清空集合 for (int i = 0; i <lists.size(); i++) { System.out.println(lists.get(i)); } System.out.println("==============================="); for (Object object : lists) { System.out.println(object); } } }
-
结果
集合长度:5 是否包含:有100 100 100 100 王健林 马云 1000.98 =============================== 100 100 王健林 马云 1000.98
-
案例2:添加对象类型
-
student类
public class Student { private String name; private int age; private char sex; public void showStudent() { System.out.println("你好我是"+name+"年龄:"+age+"性别:"+sex); } public Student() { super(); } public Student(String name, int age, char sex) { super(); this.name = name; this.age = age; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } }
-
测试类
import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { Student stu1 = new Student("马云",45,'男'); Student stu2 = new Student("马化腾",46,'男'); Student stu3 = new Student("董明珠",47,'女'); Student stu4 = new Student("章泽天",28,'女'); List list = new ArrayList(); list.add(stu1); list.add(stu2); list.add(stu3); list.add(stu1); list.add(1,stu4); // list.add("张三丰");因为add(odject)类型可以插入任意类型, Student student1 = (Student)list.get(1); student1.showStudent(); System.out.println("================================"); for (int i = 0; i < list.size(); i++) { Student student = (Student)list.get(i); student.showStudent(); } System.out.println("=============================="); list.remove(stu1);//根据对象删除 list.remove(1);//根据下标删除 for (Object object : list) { Student student2 = (Student)object; student2.showStudent(); } } }
-
结果
你好我是章泽天年龄:28性别:女 ================================ 你好我是马云年龄:45性别:男 你好我是章泽天年龄:28性别:女 你好我是马化腾年龄:46性别:男 你好我是董明珠年龄:47性别:女 你好我是马云年龄:45性别:男 ============================== 你好我是章泽天年龄:28性别:女 你好我是董明珠年龄:47性别:女 你好我是马云年龄:45性别:男 ======================== 你好我是章泽天年龄:28性别:女 你好我是董明珠年龄:47性别:女 你好我是马云年龄:45性别:男
-
案例3
-
用泛型约束集合中的元素类型
import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { Student stu1 = new Student("马云",45,'男'); Student stu2 = new Student("马化腾",46,'男'); Student stu3 = new Student("董明珠",47,'女'); Student stu4 = new Student("章泽天",28,'女'); //<Student>泛型 用于约束集合中约束的类型 List<Student> list = new ArrayList<Student>(); list.add(stu1); list.add(stu2); list.add(stu3); list.add(stu1); list.add(1,stu4); // list.add("张三丰");因为定义集合的时候通过泛型约束了集合的类型是Student,所以只能插入Student类型的数据 System.out.println("================================"); for (int i = 0; i < list.size(); i++) { Student student =list.get(i); student.showStudent(); } System.out.println("=============================="); list.remove(stu1);//根据对象删除 list.remove(1);//根据下标删除 for (Student student : list) { student.showStudent(); } } }
-
结果
你好我是章泽天年龄:28性别:女 ================================ 你好我是马云年龄:45性别:男 你好我是章泽天年龄:28性别:女 你好我是马化腾年龄:46性别:男 你好我是董明珠年龄:47性别:女 你好我是马云年龄:45性别:男 ============================== 你好我是章泽天年龄:28性别:女 你好我是董明珠年龄:47性别:女 你好我是马云年龄:45性别:男 ======================== 你好我是章泽天年龄:28性别:女 你好我是董明珠年龄:47性别:女 你好我是马云年龄:45性别:男
2.1.2 LinkedList
-
LinkedList的实现机制与ArrayList的实现机制完全不同,ArrayLiat内部以数组形式保存数组,所以查询和遍历元素快,但插入和删除需要位移性能低;LinkedList内部以链表的形式保存集合的元素,所以查询和遍历效率比ArrayList低,插入和删除元素效率高
-
LinkedList.add()源码:
-
把元素插到集合容器中的同时,将该元素记录在该元素的前、后的位置。所以插入和删除该元素不需要位移
void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
-
LinhkedList集合的get(i),是根据下标参数,先将index位置化为两半,如果index小于集合的一半就从第一个往后找直到找到index的值赋给x返回,如果大于就从集合最后的值往前找,直到找到index的值赋给x返回,这样可以减少一部分不必要的遍历,但是还是没有ArrayLiat直接找到下标位置的元素效率高。
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; } }
-
案例
import java.util.LinkedList; import java.util.List; import day02.Student; public class Test { public static void main(String[] args) { Student stu1 = new Student("马云",45,'男'); Student stu2 = new Student("马化腾",46,'男'); Student stu3 = new Student("董明珠",47,'女'); Student stu4 = new Student("章泽天",28,'女'); LinkedList<Student> list = new LinkedList<Student>(); list.add(stu1); list.add(stu2); list.addFirst(stu3);//插入最前面 list.addLast(stu4);//插入最后面 //获取第一个和最后一个对象 list.getFirst().showStudent(); list.getLast().showStudent(); for (Student student : list) { student.showStudent(); } for (int i = 0; i < list.size(); i++) { Student stu = list.get(i); stu.showStudent(); } } }
-
结果
你好我是董明珠年龄:47性别:女 你好我是章泽天年龄:28性别:女 你好我是董明珠年龄:47性别:女 你好我是马云年龄:45性别:男 你好我是马化腾年龄:46性别:男 你好我是章泽天年龄:28性别:女 你好我是董明珠年龄:47性别:女 你好我是马云年龄:45性别:男 你好我是马化腾年龄:46性别:男 你好我是章泽天年龄:28性别:女
2.1.3 vector
-
vector的add()方法的源码
public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; }
-
synchronized修饰使方法同步,此集合线程安全。
-
ArrayList和Vector的区别
- ArrayList集合和vector集合底层数组存储的操作都是一样的。ArrayList非线程安全,效率高;vector是线程安全的。
-
案例
public class Test { public static void main(String[] args) { Student stu1 = new Student("马云",45,'男'); Student stu2 = new Student("马化腾",46,'男'); Student stu3 = new Student("董明珠",47,'女'); Student stu4 = new Student("章泽天",28,'女'); //因为ArrayLiat是线程不安全的,而vector底层基本和ArrayList一致也是一个数组,并且线程安全 Vector<Student> list = new Vector<Student>(); list.add(stu1); list.add(stu2); list.add(stu3); list.add(stu1); for (Student student : list) { student.showStudent(); } } }
-
结果
你好我是马云年龄:45性别:男 你好我是马化腾年龄:46性别:男 你好我是董明珠年龄:47性别:女 你好我是马云年龄:45性别:男
数据作为方法的参数
Map<String,Student> map = new LinkedHashMap<String,Student>();
map.put(stu1.getName(), stu1);
map.put(stu2.getName(), stu2);
map.put(stu3.getName(), stu3);
map.put(stu4.getName(), stu4);
//获取到所有的键,是一个无序的set集合
Set<String> keys = map.keySet();
//创建迭代器,让无序的键排队
Iterator<String> it = keys.iterator();
while(it.hasNext()) {
//判断迭代器容器里面有没有键,如果有就取到下一个键
String key = it.next();
//根据键获取值
Student student = map.get(key);
student.showStudent();
}
System.out.println("=================================");
for (String key : keys) {
Student student = map.get(key);
student.showStudent();
}
System.out.println("===============================");
for (Student student : map.values()) {
student.showStudent();
}
}
}
* 结果
```java
你好我是马云,年龄是:45
你好我是马化腾,年龄是:40
你好我是董明珠,年龄是:47
你好我是章泽天,年龄是:28
=================================
你好我是马云,年龄是:45
你好我是马化腾,年龄是:40
你好我是董明珠,年龄是:47
你好我是章泽天,年龄是:28
===============================
你好我是马云,年龄是:45
你好我是马化腾,年龄是:40
你好我是董明珠,年龄是:47
你好我是章泽天,年龄是:28
10