Java集合/Collection个人笔记
———————————————————————————————————————————————————————
预备知识 —— 装箱、拆箱
- 集合中的元素都为引用数据类型。
- 集合只存放元素地址,不存放变量。
// 基本数据类型对应的包装类
Integer b = new Integer(5);
Float f = new Float(1.11);
Double d = new Double(1.23);
Character c = new Character('y');
Short s = new Short((short) 9);
// 将基本数据类型转换为引用数据类型叫做装箱
int x = 5;
// 将引用数据类型转换为基本数据类型叫做拆箱
test(x);
// java5 版本之前这条语句会报错
Integer i = 90;
// java5 版本之后会进行自动装箱
int i1 = i;
// 自动拆箱
———————————————————————————————————————————————————————
Java中的集合
- Java 中主要有两种集合:Collection 接口 和 Map 接口,
其中 Collection 下又包含 List 和 Set 两个子接口。
- Collection:将一组对象以集合元素的形式组织到一起,在其子接口中分别实现不同的组织方式。
- Set: Collection 的子接口,不记录元素的保存顺序 ,且 不允许有重复元素。
- List: Collection 的子接口,记录元素的保存顺序 ,且 允许有重复元素。
———————————————————————————————————————————————————————
Collection
1. Collection 与 Collections
- Collections:不属于集合,是集合类的工具类。
高度抽象出来的集合,包含了集合的基本操作和属性。 - Collection: 集合类的 上级接口(父接口),继承于它的接口主要有 List 和 Set。
*Collections 类中定义了多种集合操作方法,实现了对集合元素的排序、取极值、批量拷贝、集合结构转换、循环移位以及匹配性检查等功能。
*Arrays,不属于集合类,是数据对象的工具类。
2. Collection 中元素的存放特点
- 集合中只允许存放引用数据类型,并且存的是地址。
Collection collection = new ArrayList(); // 多态
collection.add("123");
collection.add(123);
// 此处的 123 为 Integer 类型
// 实现了自动装箱
collection.add(new Student());
// 默认封装 toString()方法
System.out.println(collection);
3. Collection 中的部分方法
- isEmpty();
判断集合中的元素是否为空。 - clear();
清空集合中的所有元素。 - size();
返回集合中的元素个数。
———————————————————————————————————————————————————————
集合的迭代器:Iterator
1. Iterator
Iterator 接口定义了对 Collection 类型对象中所含元素的遍历等增强处理功能。
- Iterator 称作迭代器,用于容器元素的遍历。
迭代器统一了对容器的访问方式,这也是接口解耦的最好体现。
// 迭代器的创建
// 所有 Collection 接口的实现类都有一个 iterator() 方法,用以返回一个 Iterator(接口)对象。
Iterator iterator = collection.iterator();
2. Iterator 接口的方法
iterator 类似于指针,指向所在元素的左边。
- boolean hasNext()
判断集合中是否存在下一个元素。 - Object next()
返回下一个元素并指向下一个位置。 - void remove()
删除元素。
3. 利用迭代器遍历集合
//遍历集合中的所有元素
while (iterator.hasNext()){
Object next = iterator.next();
System.out.println(next);
}
———————————————————————————————————————————————————————
List
1. List 的特点
- 有序,可以有重复元素。
- 和数组类似,List 可以动态增长。
- 查找元素效率高,相对的插入删除元素效率低,因为会引起其他元素位置改变。
- List 容器中的元素都对应一个整数型的序号记录其在容器中的位置,可以根据序号(索引)存取元素。
2. List 下的实现类
- List 接口下有三个实现类:
- ArrayList
- LinkedList
- Vector
// List 集合是接口,不可以创建对象
// 创建一个 List
List list = new ArrayList();
// list 特点:有序、可重复
3. List 中的方法
- List 继承了父类所有的方法。
// 向 list 中添加元素
list.add("123");
list.add(123);
list.add(123);
list.add(123);
list.add(new Object());
// list.size();
// 获取 list 的长度
list.size();
// list.get(int index);
// 获取索引为 index 的元素
list.get(3);
4. List 遍历的两种方式
(1) 迭代器遍历数据
// 迭代器遍历数据
while (iterator.hasNext()){
Object next = iterator.next();
System.out.println(next);
}
(2) for 循环遍历数据
// for循环遍历数据
for (int i = 0;i < list.size();i++){
System.out.println(list.get(i));
}
———————————————————————————————————————————————————————
ArrayList
1. ArrayList 的特点
- ArrayList 实现的是一个动态数组,允许任何符合规则的元素插入(包括 null),它的规模可变并且能像链表一样被访问。
- 每个 ArrayList 初始容量为 10,随着容器中元素增加,容器也会增加,同时会进行检查。快溢出时,就会扩容,扩容到原来的 1.5 倍(扩容因子为 1.5),所以如果明确插入元素的数量,最好指定一个初始容量值,避免过多扩容浪费时间,效率。
- ArrayList 的数据结构:数组,
数组的查询效率比较高。 - 它提供的功能类似 Vector 类但不同步,它是以 Array 方式实现的 List ,允许快速随机存取。
- 特点是读快改慢。
// 创建一个容量为 25 的 ArrayList
ArrayList<String> arrayList = new ArrayList<>(25);
2. ArrayList 的部分方法
ArrayList<Student> arrayList = new ArrayList();
// 泛型:指定集合装载同一种类型的元素,提高开发的安全性和操作性
Student stu1 = new Student("张三","男",20);
Student stu2 = new Student("李四","男",23);
Student stu3 = new Student("王五","男",21);
arrayList.add(stu1);
arrayList.add(stu2);
arrayList.add(stu3);
// 删除学生名称为李四的学生信息
for (int i = 0;i < arrayList.size();i++){
if (arrayList.get(i).getName().equals("李四")){
arrayList.remove(i);
}
}
3. ArrayList 的两种遍历方式
(1) 迭代器遍历数据
// 遍历集合数据
for (int i = 0;i < arrayList.size();i++){
System.out.println(arrayList.get(i).getName() + "\t\t"
+ arrayList.get(i).getSex() + "\t" + arrayList.get(i).getAge());
}
(2) foreach 遍历数据
// foreach 遍历数据
for (Student stu:arrayList) {
System.out.println(stu.toString());
}
———————————————————————————————————————————————————————
Vector
1. Vector 的特点
- Vector 是一种老旧的动态数组。
- Vector 是线程同步的。
- Vector 效率很低,一般不赞成使用。
- 与 ArrayList 相似,可以说 Vector 是线程安全的动态数组。操作与 ArrayList 一样。
2. Vector 的部分方法
List vect = new Vector();
Student stu1 = new Student("张三","男",20);
Student stu2 = new Student("李四","男",23);
Student stu3 = new Student("王五","男",21);
vect.add(stu1);
vect.add(stu2);
vect.add(stu3);
// 默认将元素装载到已有元素的后面一个位置
vect.add(2,new Student("赵六","男",22));
// 默认将元素装载到指定索引的位置
vect.remove(2);
// 删除集合中索引为 2 的元素
// vect.set(int index, Element e);
// 根据索引修改指定元素的数据
vect.set(0,new Student("刘七","男",19));
// vect.contains(Object o);
// 判断是否包含此对象
vect.contains(new Student("徐八","男",18));
3. Vector 的遍历
Iterator iterator = vect.iterator();
// 迭代器遍历数据
while (iterator.hasNext()){
// Object next = iterator.next();
// 因为没有指定泛型,所以默认是 Object 类型的
// 想要转成 Student 类型的对象需要进行强制转换
Student next = (Student)iterator.next();
System.out.println(next.getName() + "\t\t" + next.getSex() + "\t" + next.getAge());
}
———————————————————————————————————————————————————————
ArrayList 和 Vector 的区别
- 线程同步,Vector 线程安全,ArrayList 线程不安全。
- 效率问题,Vector 效率低,ArrayList 效率高。
- 增长数量,Vector 以 2 倍增长,ArrayList 以 1.5 倍增长(ArrayList 和 Vector 都采用线性连续存储空间,当存储空间不足的时候,ArrayList 默认增加原来的 50%,Vector 默认增加原来的一倍)。
- Vector 可以设置 capacityIncrement(capacity:容量,Increment:增加,capacityIncrement:容量增长的参数),而 ArrayList 不可以。
此处转载一篇博客园的文章
ArrayList 和 Vector 扩容
———————————————————————————————————————————————————————
LinkedList
1. LinkedList 的特点
-
LinkedList 的方法基本与 ArrayList 一致,不同的是底层的数据结构。
-
LinkedList 的数据结构:链表,
链表的增删改操作效率比较高。 -
LinkedList 实现一个链表,提供最佳顺序存取,适合插入和移除元素。
由这个类定义的链表也可以像栈或队列一样被使用。 -
特点是改快读慢。
-
LinkedList 默认装载 Object 类型。
-
底层是一个双向链表(链表中有两个属性,第一个存储数据,第二个存储下一个容器的地址)。
-
除了 ArrayList 基本方法,还额外提供了add、get、remove方法在 LinkedList 的首部或尾部。
/*
* 子类特有功能,不能多态调用
*
* addFirst(E) 添加到链表的开头
* addLast(E) 添加到链表的结尾
*
* E getFirst() 获取链表的开头
* E getLast() 获取链表的结尾
*
* E removeFirst() 移除并返回链表的开头
* E removeLast() 移除并返回链表的结尾
*/ -
非同步,不能随机访问,所有操作按照双重链表的需要执行。
-
链表中索引的操作将从开头或结尾遍历列表,以较低的代价在 List 中插入和删除。
2. LinkedList 的遍历
// 创建一个LinkedList对象
LinkedList linkedList = new LinkedList();
linkedList.add(123);
linkedList.add("12345");
linkedList.add(new Object());
linkedList.add(789);
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()){
Object next = iterator.next();
System.out.println(next);
}
———————————————————————————————————————————————————————
ArrayList 与 LinkedList 的区别
- ArrayList 与 LinkedList 的区别
-
线程安全
都是线程不安全。 -
底层数据结构
它们都是线性表。
ArrayList 基于 Object 数组(顺序存储)。
LinkedList 基于双向循环链表(链式存储)。 -
插入和删除是否受元素位置影响
ArrayList:插入删除的时间复杂度受元素位置的影响。在某个位置插入元素,第 i 个和第 i 个之后的 (n - 1) 个元素都要执行向后/向前移动一位的操作。
LinkedList:链表,插入,删除,时间复杂度不受元素位置的影响。近似 O(1),而数组近似 O(n)。 -
是否快速随机访问
ArrayList:实现了 RandomAccess 接口,支持快速随机访问。
LinkedList:不支持。随机访问就是通过元素的序号快速获取元素对象。 -
内存空间占用
ArrayList:列表的结尾会预留一定的容量空间。
LinkedList:每个元素都需要消耗比 ArrayList 更多的空间。 -
性能效率
ArrayList 在查询和修改元素时性能较高,插入和删除元素时效率较低,
LinkedList 在插入和删除元素时效率较高,查询和修改元素时效率较低。
———————————————————————————————————————————————————————
Set
1. Set 的特点
- 无序,不允许重复(访问集合中的元素只能根据元素本身访问)。
允许null的存在,但仅有一个。 - 检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
- Set 集合中的元素不按特定方式排序,只是简单的把对象加入集合中,就像往口袋里放东西。
- Set 接口没有提供额外的方法。
*任何可变对象,如果在对集合中元素进行操作时,致 e1.equals(e2) == true
*String 类中重写了 hashCode 和 equals 方法用来比较指向的字符串对象所存储的字符串是否相等。
2. Set 下的实现类
- Set 接口下有两个实现类:
- HashSet
- TreeSet
———————————————————————————————————————————————————————
HashSet
1. HashSet 的特点
- 不可重复、无序(放进去的顺序和取出来的顺序不一样)。
- HashSet 中 允许包含值为 null 的元素,但 最多只能有一个 null 元素。
- HashSet 底层实现的是 HashMap。
- 添加进去的元素存在 HashMap 的 key 中。
- HashSet 能够快速定位一个元素。
- 非同步,如果多个线程同时访问一个 HashSet,其中至少一个线程修改了该 Set,必须保持外部同步。
- 按照 Hash 算法来存储集合的元素。存取、查询性能好。
HashSet 中 不允许出现重复元素,不保证集合中元素的顺序。
2. HashSet 的遍历
HashSet hashSet = new HashSet();
hashSet.add(123);
hashSet.add(12345);
hashSet.add(123);
hashSet.add("456");
hashSet.add(123);
hashSet.add(123);
hashSet.add(789);
// 迭代器遍历数据
Iterator iterator = hashSet.iterator();
while (iterator.hasNext()){
Object next = iterator.next();
System.out.println(next);
}
/*
* 遍历结果:
* 456
* 789
* 12345
* 123
*/
———————————————————————————————————————————————————————
此处插播一条 —— 泛型
JDK1.5 之前集合中的类型不明确,装入集合的元素都被当作 Object 对待,从而失去自己的类型。所以 JDK1.5 之后引入了泛型——
泛型:指定集合装载同一种类型的元素,提高开发的安全性和操作性。
*从集合中取出时往往需要类型转换,效率低下,易出错。
解决办法:定义集合时,同时定义集合中元素的类型。
好处:增强程序的可读性和稳定性。
public class GenericDemo {
public static void main(String[] args) {
User<String> user = new User<String>();
// 创建对象时所赋予的类型就是自定义泛型的类型
user.name = "张三";
System.out.println(user.name);
}
}
// E 自定义泛型
class User<E>{
// E - 此集合中保存的元素的类型,
// 主类中创建对象时赋予了什么类型,那它就是什么类型
E name;
public E getName(){
return null;
}
}
———————————————————————————————————————————————————————
TreeSet
1. TreeSet 的特点
- 不可重复、无序(放进去的顺序和取出来的顺序不一样)。
- 内部可自定义排序,确保元素处于排序状态,支持自然(默认方式)和定制排序。
- TreeSet 底层基于 TreeMap 实现,非线程安全。
- TreeSet 通过一个 TreeMap 存储元素,添加进去的元素存在 TreeMap 的 key 中,而 value 统一使用一个 Object 对象。
- 所有 key 必须实现 Comparable 接口,并且所有 key 都应该是同一个类的对象。
实现比较器时,需要将 key 强转成比较器接口的父类 -> (Comparable<? super K>) K
*如若是自定义引用类型,需实现 Comparable 接口,并重写其中的方法(自定义比较规则),否则会报 ClassCastException 异常(未实现 Comparable 接口,向下转型失败)。 - 在将对象元素添加到 TreeSet 集合中时,会自动按照某种比较规则将其插入到有序的对象序列中,以保证 TreeSet 集合元素组成的对象序列时刻按照 “升序” 排列。
- 该集合不通过 HashCode 和 equals 函数比较元素,而是 compare 或者 CompareTo 函数来判断元素是否相等。
compare 函数通过判断两个对象的 ID ,相同 ID 为重复元素,不会被加入到集合,而是新元素会覆盖掉旧元素。
import java.util.TreeSet;
public class TreeSet_Comparable {
public static void main(String[] args) {
TreeSet<Student> treeSetStu = new TreeSet<>();
Student stu1 = new Student(20);
Student stu2 = new Student(19);
Student stu3 = new Student(21);
Student stu4 = new Student(18);
treeSetStu.add(stu1);
treeSetStu.add(stu2);
treeSetStu.add(stu3);
treeSetStu.add(stu4);
/*
* 边存放边排序
* 在存放第一个 key 时,将它作为根节点
* 从存放第二个 key 开始,每次存放时都需先与 根节点 进行比较
* 如果 根节点 小于 传入的 key,往树的左边去找
* 如果 根节点 大于 传入的 key,往树的右边去找
* 如果 根节点 等于 传入的 key,将原来的 age 覆盖(元素不可重复)
*/
System.out.println(treeSetStu);
// [Student{age=18}, Student{age=19}, Student{age=20}, Student{age=21}]
}
}
class Student implements Comparable<Student>{
// 根据二叉树规则(左中右顺序)
// 实现比较器接口
int age;
public Student(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
'}';
}
// 重写比较器接口的比较方法
@Override
public int compareTo(Student student) {
// 定义比较规则
return this.age - student.age;
}
/*
* 如果 当前 Student 的 age 小于 传入的 Student 的 age,往树的左边去找
* 如果 当前 Student 的 age 大于 传入的 Student 的 age,往树的右边去找
* 如果 当前 Student 的 age 大于 传入的 Student 的 age,将原来的 age 覆盖(元素不可重复)
*/
}
2. TreeSet 的两种遍历方式
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
// 创建 TreeSet 对象
TreeSet<String> treeSet= new TreeSet<>();
// 特点:不可重复、无序(放进去的顺序和取出来的顺序不一样)
// 内部自定排序
treeSet.add("abcf");
treeSet.add("abce");
treeSet.add("abcx");
treeSet.add("abcd");
treeSet.add("abcz");
treeSet.add("abcy");
// 迭代器遍历
Iterator<String> iterator = treeSet.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
}
// foreach 遍历
for (Integer tree : treeSet) {
System.out.println(tree);
}
}
}
/*
* 遍历结果:
* abcd
* abce
* abcf
* abcx
* abcy
* abcz
*/
———————————————————————————————————————————————————————
感谢你看到这里!Thanks♪(・ω・)ノ