集合
集合可以分为 Collection 和 Map 两类
Collection家族
JDK 不提供 Collection 接口的任何直接实现类,而是提供了更具体的子接口,如 Set 接口和 List 接口,Collection 接口框架如图所示
Collection 接口主要有三个子接口,分别是 Set 接口、List 接口和 Queue 接口,简要介绍这三个接口。
-
Set 接口
Set 实例用于存储一组不重复的元素。
-
List 接口
List 实例是一个有序集合。程序员可对 List 中每个元素的位置进行精确控制,可以根据索引来访问元素,此外 List 中的元素是可以重复的。
-
Queue 接口
Queue 中的元素遵循先进先出的规则,是对数据结构“队列”的实现。
Set接口
Set 接口继承自 Collection 接口的主要方法:
-
boolean add(Object obj)
向集合中添加一个 obj 元素,并且 obj 不能和集合中现有数据元素重复,添加成功后返回 true。如果添加的是重复元素,则添加操作无效,并返回 false。
-
void clear()
移除此集合中的所有数据元素,即将集合清空。
-
boolean contains(Object obj)
判断此集合中是否包含 obj,如果包含,则返回 true。
-
boolean isEmpty()
判断集合是否为空,为空则返回 true。
-
Iterator iterator()
返回一个 Iterator 对象,可用它来遍历集合中的数据元素。
-
boolean remove(Object obj)
如果此集合中包含 obj,则将其删除,并返回 true。
-
int size()
返回集合中真实存放数据元素的个数,注意与数组、字符串获取长度的方法的区别。
-
Object[] toArray()
返回一个数组,该数组包含集合中的所有数据元素。
Set 接口主要有两个实现类 HashSet 和 TreeSet
-
HashSet简单使用
package Collection; import java.util.HashSet; public class collection1 { public static void main(String[] args) { HashSet peopleSet = new HashSet(); peopleSet.add("Joker"); peopleSet.add("Job"); peopleSet.add("Merry"); peopleSet.add("Rose"); System.out.println("显示集合内容" + peopleSet); System.out.println("重新添加Joker是否成功:" + peopleSet.add("Joker")); System.out.println("查看Job是否存在:" + peopleSet.contains("Job")); System.out.println("删除Merry是否成功:" + peopleSet.remove("Merry")); System.out.println("再一次查看Merry是否存在:" + peopleSet.contains("Merry")); System.out.println("查看集合元素个数:" + peopleSet.size()); System.out.println("查看修改后的集合内容" + peopleSet); } }
-
TreeSet简单使用
TreeSet 类在实现了 Set 接口的同时,也实现了 SortedSet 接口,是一个具有排序功能的 Set 接口类
package Collection; import java.util.TreeSet; public class collection2 { public static void main(String[] args) { TreeSet treeSet = new TreeSet(); treeSet.add("Joker"); treeSet.add("Merry"); treeSet.add("Jony"); System.out.println(treeSet); } }
TreeSet 集合
treeSet
里面的元素不是毫无规律的排序,而是按照自然升序(这里是指“字典”里的顺序)进行了排序
比较器
-
Comparable 接口(内部比较器)
如果程序员想定义自己的排序方式,一种简单的方法就是让加入 TreeSet 集合中的对象所属的类实现 Comparable 接口,通过实现
compareTo(Object o)
方法,达到排序的目的package Collection; import java.util.Set; import java.util.TreeSet; public class Comparables { public static void main(String[] args) { //用有序的TreeSet存储学生对象 Set stuTS = new TreeSet(); stuTS.add(new Student("王云", 1)); stuTS.add(new Student("南天华", 3)); stuTS.add(new Student("刘静涛", 2)); stuTS.add(new Student("张平", 3)); //循环输出 for(Object stu:stuTS) System.out.println(stu); } } class Student implements Comparable { int stuNum = -1; //学生学号 String stuName = ""; //学生姓名 Student(String name, int num) { this.stuNum = num; this.stuName = name; } //返回该对象的字符串表示,利于输出 public String toString() { return "学号为:" + stuNum + "的学生,姓名为:" + stuName; } //实现Comparable的compareTo方法 public int compareTo(Object o) { Student input = (Student) o; //此学生对象的学号和指定学生对象的学号比较 //此学生对象学号若大则res为1,若小则res为-1,相同的话res = 0 int res = stuNum > input.stuNum ? 1 : (stuNum == input.stuNum ? 0 : -1); //若学号相同,则按照String类自然排序比较学生姓名 if (res == 0) { res = stuName.compareTo(input.stuName); } return res; } }
Comparable 接口实现了在 TreeSet 集合中的自定义排序。这种方法是通过集合元素重写
compareTo(Object o)
方法定义排序规则的。因为是在类内部实现比较,所以也通常将 Comparable 称为内部比较在使用比较器比较元素时,如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
-
Comparator 接口(外部比较器)
Comparator 接口的比较器来完成两个对象之间的比较,从而实现按比较器规则进行排序的功能。例如要比较的对象是 JDK 中内置的某个类,而这个类又没有实现 Comparable 接口,因此我们是无法直接修改 JDK 内置类的源码的,因此就不能通过重写
compareTo(Object o)
方法来定义排序规则了,而应该使用 Comparator 接口实现比较器功能package Collection; import java.util.*; //定义一个姓名比较器 class NameComparator implements Comparator { //实现Comparator接口的compare()方法 public int compare(Object op1, Object op2) { Student eOp1 = (Student)op1; Student eOp2 = (Student)op2; //通过调用String类的compareTo()方法进行比较 return eOp1.stuName.compareTo(eOp2.stuName); } } //定义一个学号比较器 class NumComparator implements Comparator { //实现Comparator接口的compare()方法 public int compare(Object op1, Object op2) { Student eOp1 = (Student)op1; Student eOp2 = (Student)op2; return eOp1.stuNum - eOp2.stuNum; } } public class TestComparator { public static void main(String[] args) { //用LinkedList存储学生对象 LinkedList stuLL = new LinkedList(); stuLL.add(new Student("王云",1)); stuLL.add(new Student("南天华",3)); stuLL.add(new Student("刘静涛",2)); //使用sort方法,按姓名比较器进行排序 Collections.sort(stuLL,new NameComparator()); System.out.println("***按学生姓名顺序输出学生信息***"); Iterator it = stuLL.iterator(); while (it.hasNext()) { System.out.println(it.next()); } //使用sort方法,按学号比较器进行排序 Collections.sort(stuLL,new NumComparator()); System.out.println("***按学生学号顺序输出学生信息***"); it = stuLL.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } } //定义学生对象,未实现Comparable接口 class Student{ int stuNum = -1; String stuName = ""; Student(String name, int num) { this.stuNum = num; this.stuName = name; } public String toString() { return "学号为:" + stuNum + "的学生,姓名为:" + stuName; } }
Iterator 迭代器
-
Iterator
Iterator 为遍历集合而生,是 Java 语言解决集合遍历的一个工具。
iterator()
方法定义在 Collection 接口中,因此所有单值集合的实现类,都可以通过iterator()
实现遍历。iterator()
的返回值是Iterator
对象,通过 Iterator 接口的hasNext()
和next()
方法即可实现对集合元素的遍历下面是Iterator接口的三个方法:
-
boolean hasNext()
判断是否存在下一个可访问的数据元素。
-
Object next()
返回要访问的下一个数据元素,通常和
hasNext()
在一起使用。 -
void remove()
从迭代器指向的 Collection 集合中移除迭代器返回的上一个数据元素。
-
-
Iterator 使用
package Collection.Iterator; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class test1 { public static void main(String[] args) { Set hashSet = new HashSet(); hashSet.add("Hamapi"); hashSet.add("Gui"); hashSet.add("Java"); hashSet.add("Python"); //Iterator迭代器 Iterator it = hashSet.iterator(); while (it.hasNext()){ //通过hashNext判断集合是否还有元素 System.out.println("课程:" + it.next()); //通过next获取当前元素进行输出 } } }
Iterator 接口的
hasNext()
方法判断集合中是否还有对象元素,再通过该接口的next()
方法获取这个对象元素List接口
List 是 Collection 接口的子接口,List 中的元素是有序的,而且可以重复。List 集合中的数据元素都对应一个整数形式的序号索引,记录其在集合中的位置,可以根据此序号存取元素
List 接口继承自 Collection 接口,除了拥有 Collection 接口所拥有的方法外,还拥有下列方法:
-
void add(int index,Object o)
在集合的指定 index 位置处,插入指定的 o 元素。
-
Object get(int index)
返回集合中 index 位置的数据元素。
-
int indexOf(Object o)
返回此集合中第一次出现的指定 o 元素的索引,如果此集合不包含 o 元素,则返回-1。
-
int lastIndexOf(Object o)
返回此集合中最后出现的指定
o
元素的索引,如果此集合不包含o
元素,则返回-1。 -
Object remove(int index)
移除集合中 index 位置的数据元素。
-
Object set(int index,Object o)
用指定的 o 元素替换集合中 index 位置的数据元素。
ArrayList
ArrayList 实现了 List 接口,其底层采用的数据结构是数组,另一个 List 接口的实现类是 LinkedList,它在存储方式上采用链表进行链式存储
package Collection.Lists; import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class ArrayLists { public static void main(String[] args) { Scanner input = new Scanner(System.in); List array = new ArrayList(); array.add("中国"); array.add("日本"); array.add("加拿大"); array.add("英国"); for (Object a : array){ System.out.print(a + "\t\t"); } System.out.println(); System.out.print("输入查找的索引,范围(0~3):"); System.out.println(array.get(input.nextInt())); } }
通过调用 ArrayList 接口的
get(int index)
方法获取指定位置的元素,并输出该对象的信息。LinkedList
LinkedList 的底层是链表。
一般而言,对于“索引访问”较多的集合操作建议使用 ArrayList,而对于“增删”较多的集合操作建议使用 LinkedList。
LinkedList 接口除了拥有 ArrayList 接口提供的方法外,还增加了如下一些方法。
-
void addFirst(Object o)
将指定数据元素插入此集合的开头。
-
void addLast(Object o)
将指定数据元素插入此集合的结尾。
-
Object getFirst()
返回此集合的第一个数据元素。
-
Object getLast()
返回此集合的最后一个数据元素。
-
Object removeFirst()
移除并返回此集合的第一个数据元素。
-
Object removeLast()
移除并返回此集合的最后一个数据元素。
将上面ArrayList的代码进行修改
package Collection.Lists; import java.util.LinkedList; import java.util.Scanner; public class LinkedLists { public static void main(String[] args) { Scanner input = new Scanner(System.in); LinkedList array = new LinkedList(); array.add("中国"); array.add("日本"); array.add("加拿大"); array.add("英国"); for (Object a : array){ System.out.print(a + "\t\t"); } System.out.println(); System.out.print("输入查找的索引,范围(0~3):"); System.out.println(array.get(input.nextInt())); System.out.println("第一个元素是:" + array.getFirst()); System.out.println("最后一个元素是:" + array.getLast()); array.addFirst("新西兰"); System.out.println("新添加的第一个元素:" + array.getFirst()); } }
-
Map家族
Map 接口定义了存储和操作一组“键(key)值(value)”映射对的方法。Map 接口和 Collection 接口的本质区别在于,Collection 接口里存放的是一系列单值对象,而 Map 接口里存放的是一系列
key-value 对对象。Map 中的 key 不能重复,每个 key 最多只能映射到一个值
Map 接口框架如图所示
HashMap 和 Hashtable 是 Map 接口的实现类
泛型
泛型是指在定义集合的同时也定义集合中元素的类型,需要“< >”进行指定,其语法形式如下
集合<数据类型> 引用名 = new 集合实现类<数据类型> ();
需要注意,使用泛型约束的数据类型必须是对象类型,而不能是基本数据类型。
如下就限制了 List 集合中只能存放 String 类型的元素
List<String> list = new ArrayList<String>();
下面举一个例子,存放车车辆
package Collection.Lists;
import java.util.ArrayList;
import java.util.List;
public class Lists1 {
public static void main(String[] args) { List<Vehicle> arrayLists = new ArrayList<Vehicle<>(); a
rrayLists.add(new Vehicle("大力士",1));
arrayLists.add(new Vehicle("保时捷",6262));
arrayLists.add(new Vehicle("宝马",1554));
arrayLists.add(new Vehicle("法拉利",199));
for (Object o : arrayLists){ System.out.println(o);
} }}
class Vehicle{
String name = "";
private int no = 0;
public Vehicle(String name,int no){ setName(name);
setNo(no);
}
public String getName() { return name;
}
public void setName(String name) { this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) { this.no = no;
}
@Override
public String toString() { return "车类型:" + name + "\t车牌号" + no;
}
public void show(){ getName();
getNo();
}
}
List<Vehicle> arrayLists = new ArrayList<Vehicle>();
的作用是使用泛型创建 ArrayList 集合arrayLists,且集合中元素必须是 Vehicle 类及其子类。如果向这个集合中添加其他的类型,编译器会报错。
工具类
Collection
Collections 工具类,是集合对象的工具类,提供了操作集合的工具方法,如排序、复制、反转和查找等方法,如下所示。
-
void sort(List list)
根据数据元素的排序规则对 list 集合进行排序,其中的排序规则是通过内部比较器设置的。例如 list 中存放的是 obj 对象,那么排序规则就是根据 obj 所属类重写内部比较器 Comparable 中的
compareTo()
方法定义的。 -
void sort(List list, Comparator c)
根据指定比较器中的规则对 list 集合进行排序。通过自定义 Comparator 比较器 c,可以实现按程序员定义的规则进行排序。
-
void shuffle(List list)
对指定 list 集合进行随机排序。
-
void reverse(List list)
反转 list 集合中数据元素的顺序。
-
Object max(Collection coll)
根据数据元素的自然顺序,返回给定 coll 集合中的最大元素。该方法的输入类型为 Collection 接口,而非 List 接口,因为求集合中最大元素不需要集合是有序的。
-
Object min(Collection coll)
根据数据元素的自然顺序,返回给定 coll 集合的最小元素。
-
int binarySearch(List list,Object o)
使用二分查找法查找 list 集合,以获得 o 数据元素的索引。如果此集合中不包含 o 元素,则返回-1。在进行此调用之前,必须根据 list 集合数据元素的自然顺序对集合进行升序排序(通过
sort(List list)
方法)。如果没有对 list 集合进行排序,则结果是不确定的。如果 list 集合中包含多个元素“等于”指定的 o 元素,则无法保证找到的是哪一个,这里说的“等于”是指通过equals()
方法判断相等的元素。 -
int indexOfSubList(List source,List target)
返回指定源集合 source 中第一次出现指定目标集合 target 的起始位置,换句话说,如果 target 是 source 的一个子集合,那么该方法返回 target 在 source 中第一次出现的位置。如果没有出现这种集合间的包含关系,则返回-1。
-
int lastIndexOfSubList(List source,List target)
返回指定源集合 source 中最后一次出现指定目标集合 target 的起始位置,如果没有出现这样的集合,则返回-1。
-
void copy(List dest,List src)
将所有数据元素从 src 集合复制到 dest 集合。
-
void fill(List list,Object o)
使用 o 数据元素替换 list 集合中的所有数据元素。
-
boolean replaceAll(List list,Object old,Object new)
使用一个指定的 new 元素替换 list 集合中出现的所有指定的
old
元素。 -
void swap(List list,int i,int j)
在 list 集合中,交换 i 位置和 j 位置的元素。
具体实现使用代码体现作用
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollections {
public static void main(String[] args) {
List list = new ArrayList();
list.add("w");
list.add("o");
list.add("r");
list.add("l");
list.add("d");
System.out.println("排序前: " + list);
System.out.println("该集合中的最大值:" + Collections.max(list));
System.out.println("该集合中的最小值:" + Collections.min(list));
Collections.sort(list);
System.out.println("sort排序后: " + list);
//使用二分查找,查找前须保证被查找集合是自然有序排列的
System.out.println("r在集合中的索引为: " + Collections.binarySearch(list, "r"));
Collections.shuffle(list);
System.out.println("再shuffle排序后: " + list);
Collections.reverse(list);
System.out.println("再reverse排序后: " + list);
Collections.swap(list, 1, 4);
System.out.println("索引为1、4的元素交换后:" + list);
Collections.replaceAll(list, "w", "d");
System.out.println("把w都换成d后的结果: " + list);
Collections.fill(list, "s");
System.out.println("全部填充为s后的结果: " + list);
}
}
Arrays
Arrays 类是操作数组的工具类,和 Collections 工具类相似,Arrays 类主要有以下功能:
- 对数组进行排序。
- 给数组赋值。
- 比较数组中元素的值是否相等。
- 进行二分查找。
详情可见往期博客文章数组
HashMap
Map 接口定义了存取“键值对”的方法,Map 接口的常用方法如下。
-
Object put(Object key,Object value)
将指定键值对(key 和 value)添加到 Map 集合中,如果此 Map 集合以前包含一个该键 key 的键值对,则用参数 key 和 value 替换旧值。
-
Object get(Object key)
返回指定键 key 所对应的值,如果此 Map 集合中不包含该键 key,则返回 null。
-
Object remove(Object key)
如果存在指定键 key 的键值对,则将该键值对从此 Map 集合中移除。
-
Set keySet()
返回此 Map 集合中包含的键的 Set 集合。在上面的程序最后添加下面的语句:
System.out. println(domains.keySet());
,则会输出[com, edu, org, net]
。 -
Collection values()
返回此 Map 集合中包含的值的 Collection 集合。在上面的程序最后添加下面的语句:
System.out.println(domains.values());
,则会输出[工商企业,教研机构,非营利组织,网络服务商]
。 -
boolean containsKey(Object key)
如果此 Map 集合包含指定键 key 的键值对,则返回 true。
-
boolean containsValue(Object key)
如果此 Map 集合将一个或多个键 key 对应到指定值,则返回 true。
-
int size()
返回此 Map 集合的键值对的个数。
代码解释
package Collection.Map;
import java.util.HashMap;
import java.util.Map;
public class TestHashMap {
public static void main(String[] args) {
Map tains = new HashMap();
tains.put(".top", "博客网站");
tains.put(".com", "企业网站");
tains.put(".org", "非盈利网站");
tains.put(".vip", "高级网站");
System.out.println(tains);
String id = (String) tains.get(".org");
System.out.println("获取后缀为.org的网站为:" + id);
tains.remove(".vip");
System.out.println("移除后缀为.vip的网站:" + tains);
System.out.println("显示后缀:" + tains.keySet());
System.out.println("输出包含后缀的网站:" + tains.values());
System.out.println("该集合中是否包含.vip的网站:" + tains.containsKey(".cn"));
System.out.println("该集合中是否包含.com的网站:" + tains.containsKey(".com"));
System.out.println(tains.containsValue(".com"));
System.out.println("集合键值大小" + tains.size());
}
}
Map遍历
遍历 Collection 的通用方法——使用增强 for 或者迭代器 Iterator,并且知道 Collection 是单值形式元素的集合,而 Map 是键值对形式的元素集合,及具体实现方法就是Map -> 转换为单值集合 -> 使用增强 for 或者迭代器 Iterator 遍历
接下里例子会解释
package Collection.Map;
import java.util.*;
public class TestMap {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("k1", "v1");
map.put("k2", "v2");
map.put("k3", "v3");
//将Map中的key全部提取出来
Set<String> keys = map.keySet();
System.out.println("使用迭代器遍历");
Iterator<String> keyIter = keys.iterator();
while (keyIter.hasNext()) {
//获取map的每个key
String key = keyIter.next();
//根据key获取对应的value
String value = map.get(key);
System.out.println(key + "," + value);
}
System.out.println("使用增强for遍历");
for (String key : keys) {
//根据key获取对应的value
String value = map.get(key);
System.out.println(key + "," + value);
}
}
}
Map 中的每一组 key-value
对称为一个 entry 对象,即 entry=key+value。Map 接口提供了获取 Map 中全部 entry 对象的方法,因此就可以先获取全部的 entry,然后再提取 entry 对象中的 key 和 value,如下面这个代码案例
package Collection.Maps;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class TestMap2 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("k1", "v1");
map.put("k2", "v2");
map.put("k3", "v3");
//获取Map的全部entry对象
Set<Map.Entry<String, String>> entries = map.entrySet();
//遍历entry集合
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "," + value);
}
}
}
自动拆箱和装箱使用
包装类
Java 提供了八个包装类,分别对应于八个基本数据类型,并且包装类既然是类,因此也拥有方法和属性等类的特征
基本类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
double | Double |
char | Character |
boolean | Boolean |
自动拆箱和装箱使用
动拆箱和装箱看起来非常简单,也很容易理解,但是在使用过程中仍然需要注意一些细节。尤其要注意两个包装类对象之间使用 == 运算符进行比较的情况。
package Collection.Box;
public class TestBox {
public static void main(String[] args) {
Integer stuAgeI1 = 23;
System.out.println("过年了,年龄增长了一岁,现在年龄是:" + (stuAgeI1 + 1));
Integer stuAgeI2 = 23;
System.out.println("stuAgeI1 == stuAgeI2(值均为23)的结果是:" + (stuAgeI1 == stuAgeI2));
stuAgeI1 = 323;
stuAgeI2 = 323;
System.out.println("stuAgeI1 == stuAgeI2(值均为323)的结果是:" + (stuAgeI1 == stuAgeI2));
System.out.println("stuAgeI1.equals(stuAgeI2)(值均为323)的结果是:"
+ (stuAgeI1.equals(stuAgeI2)));
}
}
我们可以看到stuAgeI1 和 stuAgeI2 这两个对象中存的值均为 23 时,使用==
进行比较,其结果为 true,而当这两个对象的值为 323 时,其结果却为 false 了,这是因为例如 Byte、Integer 和 Long 这些包装类都缓存了数值在-128 ~+127 之间的对象,自动装箱的时候,如果对象值在此范围之内,则直接返回缓存的对象,只有在缓存中没有的时候再去创建一个对象。
当第一次比较 stuAgeI1 和 stuAgeI2 这两个对象时,因为其值在-128 ~+127 之间,所以这两个对象都是直接返回的缓存对象,使用 == 比较时结果为 true
。而第二次比较 stuAgeI1 和 stuAgeI2 这两个对象时,其值超出了-128 ~+127 的范围,需要通过 new 方法创建两个新的包装类对象,所以再使 == 比较时结果为 false
总结
- JDK 中的集合可以分为 Collection 家族和 Map 家族两大类,Collection 集合中存放的是单值元素组成的集合,Map 集合中存放的由键值对元素组成的集合。
- List 集合中的元素是有序的、可重复的;Set 集合中的元素不可重复。
- Comparable 接口称为内部比较器,Comparator 接口称为外部比较器,开发者既可以通过重写前者中的
compareTo()
方法对元素进行排序,也可以通过重写后者的compare()
方法对元素进行排序。 - Collection 接口中定义了
iterator()
方法,其返回值是 Iterator 对象。通过 Iterator 对象的hasNext()
和next()
方法可以实现对集合元素的遍历。 - 可以使用增强 for 循环和迭代器对 Collection 集合进行遍历;在遍历 Map 集合时,需要先将 Map 转为单值集合(key 集合、value 集合或者 entry 集合),然后再遍历。
- JDK 为八个基本类型的数据提供了相应的包装类,并且为基本数据类型和包装类之间的转换提供了自动装箱和自动拆箱功能。
- 使用泛型可以限制集合中元素的类型,避免了在集合中进行强制类型转换等繁琐操作。
- List 集合中的元素是有序的、可重复的;Set 集合中的元素是无序的、不可重复的。
评论区