![]()
# 集合概念
集合是一种特殊类,这些类可以存储任意类对象,并且长度可变,这些集合类都位于 java.util 中,使用的话必须导包
按照存储结构可以分为两大类 单列集合 Collection
双列集合 Map
两种 区别如下
Collection
单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的的子接口, List Set
1 2 3
| List 接口 特点是元素有序可重复 它的实现类有 ArrayList LinkedList
Set 接口 特点是元素无序且不可重复它的实现类有 HashSet TreeSet
|
Map
双列集合类的根接口,用于存储具有 键 (Key) 值 (Value) 隐射关系的元素, 每个元素都包 含一对键值,其中键值不可重复且每个键 最多只能映射到到一个值,在使用 Map 集合时 可以通过指定的 Key
找到对应的 Value
, 通一个人的学号找到学生的名字一样没有爸爸接口,只要爷爷接口 Mpa 实现类有 HashMap TreeMap
# Collection 单列集合根接口
单列集合的父接口,它定义了单例集合 ( List Set
) 通用的一些方法,这样方法可用于操作所有的单列集合,,在开发中,往往很少直接使用 Collcetion 接口进行开发基本上都是使用其子接口,子接口主要有 List、Set、Queue和 SortedSeto
常用 API
![]()
# List 接口
List
接口继承父接口 Collection
接口,是单列集合的一个重要分支,它允许出现重复的元素,所有元素都是以一种线性的方式存储的,通过索引访问 List
集合中的指定元素,它的特点是有序 ** 即元素 存入和取出的 顺序一致 **
List
集合常用方法,上方父接口 Collection
方法同样可以继承使用
List 的所有实现类都可以通过调用这些方法操作集合元素
方法声明 | 功能描述 |
---|
void add ( int index, Object element ) | 将元素 element 插入在 List 集合的 index 处 |
boolean addAll ( int index, Collection c ) | 将集合所包含的所有元素插入到 List 集合的 index 处 |
Object get ( int index ) | 返回集合索引 inde 处的元素 |
Object remove ( int index ) | 删除集合索引 inde 处的元素 |
Object set ( int index, Object element | 将集合索引 indx 处 元素替换成 element 对象,并将替换后的元素返回 |
int indexOf ( Object o ) | 返回对象 o 在 List 集合中出现的位置索引 |
int lastlndexOf ( Object o ) | 返回对象 o 在 List 集合中最后一次出现的位置索引 |
List subList ( int fromlndex, int tolndex ) | 返回从索引 fromindex (包括) 到 toindex (不包括) 处所有元素组成的子集合 |
# ArrayList 实现类 查询 有序可重复
ArrayList
是 List
接口的一个实现类,它是程序中一种常见的集合,在 ArrayList 内部封装了一个长度可变的数组对象,当存入的元素
超过数组长度时 ArrayList 会在内存中分配一个更大的数组来存储这些元素,因此可以将 ArrayList 集合看作一个长度灵活可变的数组
它的大部分方法都是继承父类 Collection
和 List
接口的 其中 add () 方法 和 get () 分别实现元素的存入和取出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import java.util.ArrayList;
public class h { public static void main(String[] args) { ArrayList List = new ArrayList(); List.add(3); List.add("你好"); List.add("世界"); System.out.println("集合的长度:"+List.size()); System.out.println("第2个元素是:"+List.get(1)); } }
---------------------------------------------
输出:
集合的长度:3 第2个元素是:你好
|
![]()
注意 :
由于 Aaytist 集合的底层使用一个数组来保存元素,在增加或删除指定位置的元素时,会创建新的数组,效率很低 因此不适合大量的增加删除操作,
因为这种数组的结构允许程序通过索引的方式来访问问元素,所以 ArrayList 集合查找元素很方法
# LinkedList 实现类 增删 有序可重复
LinkedList 集合内部维护了一个双向循环表,链表中的每一个元素都使用了引用的方式来记住它的前一个元素或后一个元素,从而可以将所有的元素彼此连接起来,LikedList 集合进行元素的增加删除操作时效率很高,
常用方法
![]()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import java.util.LinkedList;
public class g { public static void main(String[] args) { LinkedList link = new LinkedList(); link.add(3); link.add("你好"); link.add("世界"); System.out.println(link.toString()); System.out.println(link); link.add(3,"saber"); link.addFirst("插入"); System.out.println(link.getFirst()); link.remove(2); System.out.println("会输出"+link); link.removeFirst(); System.out.println(link); } }
使用 LinkedList 对元素进行增加和删除操作是非常便捷的
----------------------------------------------
输出:
[3, 你好, 世界] [3, 你好, 世界] 插入 会输出 [插入, 3, 世界, saber] [3, 世界, saber]
|
# lterator 接口 遍历迭代器
接口 Iterator
接口也是集合中的一员,但是它与 Collection Map 有所不同,Collection 接口和 Map 接口主要是用于存储元素 而
Iteratore 主要用于迭代访问 (即遍历) Collection
中的元素,因此 Iteratior 对象也称迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 代码含义:
hasNext()方法用于判断集合中是否还有下一个元素,如果有则返回true,否则返回false。如果返回true, 则可以使用next()方法取出下一个元素,并将其赋值给一个Object类型的变量obj。最后,使用System.out.println()方法将obj打印出来。 整个过程会一直重复,直到集合中的所有元素都被遍历完毕。因此,这段代码的作用是遍历ArrayList集合中的所有元素,并将它们打印出来。
注意:
通过next()方法获取元素时 必须保证获取元素的存在,否则会抛出 NoSuchElementException 异常 (无搜索元素异常)
---------------------------------------------
import java.util.ArrayList; import java.util.Iterator; public class f { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("张三"); list.add("李四"); list.add("王五"); list.add("赵六"); Iterator it = list.iterator(); while (it.hasNext()){ Object obj = it.next(); System.out.println(obj); } } }
-----------------------------------------------
输出:
张三 李四 王五 赵六
|
# 迭代判断删除
使用迭代器对集合中的元素进行迭代时,如果调用了集合对象的 remover()
方法删除元素,那么继续使用迭代器会出现异常,下面通过案例来演示说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import java.util.Locale;
public class e { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("张三"); list.add("李四"); list.add("王五"); list.add("赵六"); Iterator it = list.iterator(); while (it.hasNext()){ Object obj = it.next(); if ("张三".equals(obj)) list.remove(obj); } System.out.println(list); } }
|
上述代码会报出错误,原因是迭代器在运行期间删除了元素,导致迭代次数发生了变化,迭代结果不准确,解决方案
![]()
(1) 找到对应的名称字符删除后就使用 breack 语句跳出循环不再继续迭代,
1 2 3 4 5
| while (it.hasNext()){ Object obj = it.next(); if ("张三".equals(obj)) list.remove(obj); break;
|
(2) 使用迭代器本身的删除方法去进行删除,删除后所导致的迭代次数变化,对于迭代器本身是可预测的
1 2 3 4 5
| while (it.hasNext()){ Object obj = it.next(); if ("张三".equals(obj)) list.remove(); }
|
# foreach 循环
Iterator
虽然可以遍历集合中的元素,但是写法比较繁琐,简化书写提供了 foreach
循环,也称 增强 for 循环,它可以遍历数组或集合中的元素
1 2 3 4 5 6 7 8
| 语法格式:
for(容器中元素类型 临时变量: 容器变量){ 执行语句 }
和for循环相比foreach不再需要获得容器的长度,也不再需要根据索引访问,它会根据索引去访问容器的元素 并且自动遍历容器中的每个元素
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import java.util.ArrayList; public class foreach { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("张三"); list.add("李四"); list.add("王五"); list.add("赵六"); for (Object obj:list){ System.out.println(obj); } } }
--------------------------------
输出:
张三 李四 王五 赵六
|
# 循环局限性
当使用 foreach 循环遍历集合和数组时,只能访问集合中的元素,不能对其中的元素进行修改,下面是一个 String
类型的数组,演示 foreach
局限性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class foreach { static String[] strrs = {"aaa","ddd","ccccc"}; public static void main(String[] args) { for(String str: strrs){ str="ddd"; } System.out.println("foreach循环修改后的数组"+strrs[0]+","+strrs[1]+","+strrs[2]); for (int i= 0;i<strrs.length;i++){ strrs[i]="ddd"; } System.out.println("循环修改后的数组"+strrs[0]+","+strrs[1]+","+strrs[2]); } }
-----------------------------------
输出:
foreach循环修改后的数组aaa,ddd,ccccc 循环修改后的数组ddd,ddd,ddd
|
foreach 循环 str=“ddd”
只是将临时的变量指向一个新的字符串并不能修改元素,for 循环则是可以通过索引的方式对数组中的元素进行修改的
# Set 接口
Set 接口和 List 接口一样,同样继承 Collection
接口,方法和他基本一致,功能上并没有扩充,反而更加严格,它的 List 接口不同在于,Set 接口元素无序且不重复
实现类
- **HashSet **: 根据对象的散列值来确定元素在集合中存储的位置,具有良好的存取和查找功能
- TreeSet : 以二叉树方式存储元素,它可以实现对元素的排序
# HashSet 实现类无序无重复
存储元素不可重复 意味着没有相同的,并且元素无序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import java.util.HashSet; import java.util.Iterator;
public class foreach { static String[] strrs = {"aaa","ddd","ccccc"}; public static void main(String[] args) { HashSet set = new HashSet(); set.add("张三"); set.add("李四"); set.add("王五"); set.add("王五"); Iterator it = set.iterator(); while (it.hasNext()){ Object obj = it.next(); System.out.println(obj); } } }
-----------------------------------------------
输出:
李四 张三 王五
|
注意:
取出元素和添加元素并不一致,并且重复添加的元素只出现了一次,它之所以可以确保不出现重复的元素,做了很多工作,add 添加元素时,首先调用存入对象的 hashCode()
方法获得对象的散列值,然后根据元素的散列值计算出特有的存储位置 **, 散列值还是看数据类型是否相等的 **, 只要数据类型相等,里面数字相等那么就是一样的,如果出现 String
字符串的 100 和 int
类型的 100 其实是不同的,因为数据类型不同,并且 equals
使用的前提也是相同数据类型比较字符串类型一致,详见 String 字符串 Random 数字运算、
上面是不相同的情况,相同情况则是计算哈希后,进行 equals
比较,如果比较存在,则是舍弃 没有则是加入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import java.util.HashSet; import java.util.Iterator;
public class foreach { static String[] strrs = {"aaa","ddd","ccccc"}; public static void main(String[] args) { HashSet set = new HashSet(); set.add("张三"); set.add("李四"); set.add("王五"); set.add("王五"); set.add("100"); set.add(100); Iterator it = set.iterator(); while (it.hasNext()){ Object obj = it.next(); System.out.println(obj); } } }
------------------------------------
两个内容相同类型不同,散列值也是不同 会保存下来
输出:
李四 100 张三 100 王五
|
# HashSet 存储 Class 类
将字符串存入 HshSet
时 String 类已经重写了 hashCode
和 equals
方法,下面演示存储自定义的 Class
类 的结果
未改写 hashCode 和 equals () 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| import java.util.HashSet; class Student{ String id; String name;
public Student(String id, String name) { this.id = id; this.name = name; } public String toString(){ return id+":"+name; } } public class foreach { static String[] strrs = {"aaa","ddd","ccccc"}; public static void main(String[] args) { HashSet set = new HashSet(); Student stu1 = new Student("1","杜甫"); Student stu2 = new Student("2","李白"); Student stu3 = new Student("2","李白"); set.add(stu1); set.add(stu2); set.add(stu3); System.out.println(set); } }
---------------------------------------------
输出:
[2:李白, 2:李白, 1:杜甫]
注意:
在Java中,当我们使用System.out.println()方法输出一个对象时,实际上会自动调用该对象的 toString ()方 法来获取其字符串表示形式。因此,在这段代码中,当我们使用System.out.println(set)输出HashSet对象时 实际上会自动调用每个Student对象的toString()方法来获取其字符串表示形式,并将它们拼接成一个字符串输 出。虽然你没有显式调用toString()方法,但它确实被隐式调用了。 所以上面代码我们没有调用实际上啊是自动 调用了
----------------------------------------------------------
运行结果出现了两个相同的李白2,本来应该被认为是重复元素,不允许输出的,为什么没有去掉是因为在定义 Class类时 没有重写hashCode和equals()方法
|
已改写 hashCode 和 equals () 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| import java.util.HashSet; class Student{ String id; String name; public Student(String id, String name) { this.id = id; this.name = name; } public String toString(){ return id+":"+name; } public int hashCode(){ return id.hashCode(); } public boolean equals(Object obj){ if (this==obj){ return true; } if (!(obj instanceof Student)){ return false; } Student stu = (Student) obj; boolean b = this.id.equals(stu.id); return b; } } public class foreach { static String[] strrs = {"aaa","ddd","ccccc"}; public static void main(String[] args) { HashSet set = new HashSet(); Student stu1 = new Student("1","杜甫"); Student stu2 = new Student("2","李白"); Student stu3 = new Student("2","李白"); set.add(stu1); set.add(stu2); set.add(stu3); System.out.println(set); } }
----------------------------------------
输出:
[1:杜甫, 2:李白]
注意:
Student类重写了Object类的hashCode()返回 id 属性的散列值还有 equals 并且在 equals 方法比较对象 的id属性值是否相等并返回结果 HashSet 集合添加元素时,因为改写了 hashCode 方法所以 add 添加时 会进行比较发现散列值相同而且 stue.equsls(stu3) 返回true 集合认为两个参数相等因为重复的被去掉了
|
# LinkedHashSet 实现类存取有序无重复
HashSet
集合存储的元素是无序的,如果想让元素存取顺序一致,那么就使用 LinkedHashSet
它是 HashSet
的子类,它和 LinkdList
一样 使用双向链表来维护内部元素关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import java.util.Iterator; import java.util.LinkedHashSet;
public class foreach { public static void main(String[] args) { LinkedHashSet set = new LinkedHashSet(); set.add("张三"); set.add("李四"); set.add("王五"); set.add("王五"); Iterator it = set.iterator(); while (it.hasNext()){ Object obj = it.next(); System.out.println(obj); } } }
-------------------------------------------------
输出:
张三 李四 王五
|
# TreeSet 实现类有序无重复
为了对集合的元素进行排序, Set
接口提供了另一个可以对 HashSet 集合中元素排序的类 —— TreeSet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import java.util.TreeSet;
public class foreach { public static void main(String[] args) { TreeSet set = new TreeSet(); set.add(1); set.add(1); set.add(3); set.add(5); set.add(6); System.out.println(set); } }
----------------------------------------------
输出:
[1, 3, 5, 6]
元素会自动排序并且没有存在重复
TreeSet 集合之所以可以对添加元素排序,是因为元素的类实现了 Comparable 接口 (基本类型的包装类 String类都实现了该接口) Comparable 强行对实现它的每个类的对象进行整体排序 这种排序被称为自然排序Comparable接口的compareTo()方法被称为自然比较方法!
|
什么是 comparTo () 方法
Comparable 接口是 Java 中的一个接口,用于实现对象之间的比较。其中,compareTo () 方法是 Comparable 接口中的一个方法,用于比较当前对象与另一个对象的大小关系。
compareTo () 方法的返回值为 int 类型,表示当前对象与另一个对象的大小关系。如果当前对象小于另一个对象,则返回负整数;如果当前对象等于另一个对象,则返回 0;如果当前对象大于另一个对象,则返回正整数。
compareTo () 方法的作用是用于实现对象之间的排序。在 Java 中,如果一个类实现了 Comparable 接口,就可以使用 Collections.sort () 方法或 Arrays.sort () 方法对该类的对象进行排序。在排序过程中,会调用 compareTo () 方法来比较对象之间的大小关系,从而实现排序。
需要注意的是,如果一个类实现了 Comparable 接口,就必须实现 compareTo () 方法,否则会编译错误。另外,compareTo () 方法的实现应该满足一定的规则,例如具有传递性、反对称性等,否则可能会导致排序结果不正确
# TreeSet 存储 Class 类
如果同 HashSet()
一样存储 Class 类,TreeSet 集合不会去进行排序,Class 类对象必须实现 Comparable
接口并重写 compareTo
方法实现对象元素的顺序存取 想对添加的元素进行排序就先重写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| import java.util.TreeSet;
class Student implements Comparable<Student>{ private String id; private String name;
public Student(String id, String name) { this.id = id; this.name = name; } public String toString(){ return id+":"+name; } public int compareTo(Student o){ return 0 ;
} } public class foreach { public static void main(String[] args) { TreeSet set = new TreeSet(); set.add(new Student("1","张三")); set.add(new Student("2","李白")); set.add(new Student("3","毒品")); System.out.println(set); } }
---------------------------------------------
输出:
return 0
[1:张三]
return 1
[1:张三, 2:李白, 3:毒品]
return -1
[3:毒品, 2:李白, 1:张三]
|
除了自然排序还有另一种排序方法;即实现 Comparator
接口 重写 compare()
方法 equals()
方法
但是由于所有的类默认继承 Object 而 Object 又存在 equals()
所以自定义比较器类时,不用重写 equals
方法,只需要重写 compare()
方法这种排序称为比较器排序
通过自定义 Class
类对象 通过比较器存入 TreeSet
集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import java.util.Comparator; import java.util.TreeSet;
class Student { private String id; private String name;
public Student(String id, String name) { this.id = id; this.name = name; } public String toString(){ return id+":"+name; } } public class foreach { public static void main(String[] args) { TreeSet set = new TreeSet(new Comparator() { @Override public int compare(Object o1, Object o2) { return -1; } }); set.add(new Student("1","张三")); set.add(new Student("2","李白")); set.add(new Student("3","毒品")); System.out.println(set); } }
--------------------------------------------
输出:
[3:毒品, 2:李白, 1:张三]
|