文章目录
- 说明
- 一、泛型
- Collection
- List
- ArrayList
- Map
- HashMap
- Set 和 HashSet
说明
Java 使用集合来组织和管理对象,本节我们重点讲解泛型和集合。主要介绍 Collection、List、ArrayList、Map、HashMap、Set 和 HashSet、Collections、算法等内容。
一、泛型
泛型即参数化类型,也就是说数据类型变成了一个可变的参数,在不使用泛型的情况下,参数的数据类型都是写死了的,使用泛型之后,可以根据程序的需要进行改变。
定义泛型的规则:
- 只能是引用类型,不能是简单数据类型。
- 泛型参数可以有多个。
- 可以用使用
extends
语句或者super
语句 如<T extends superClass>
表示类型的上界,T
只能是superClass
或其子类,<K super childClass>
表示类型的下界,K
只能是childClass
或其父类。 - 可以是通配符类型,比如常见的
Class<?>
。单独使用 ? 可以表示任意类型。也可以结合extends
和super
来进行限制。
定义泛类型
/*
使用T代表类型,无论何时都没有比这更具体的类型来区分它。
如果有多个类型参数,我们可能使用字母表中T的临近的字母,比如S。
*/
class Test<T> {
private T ob;
/*
定义泛成员变量,定义完类型参数后,可以在定义位置之后的方法的任意地方使用类型参数,
就像使用普通的类型一样(什么是类型,这不是脑残问题么)
注意:父类定义的类型参数不能被子类继承
*/
//构造函数,构造函数的作用
public Test(T ob){
this.ob = ob;
}
//getter 方法,该方法的作用
public T getOb(){
return ob;
}
//setter 方法
public void setOb(T ob){
this.ob = ob;
}
public void showType(){
//这个ob是指哪个
System.out.println("T的实际类型是:"+ob.getClass().getName());
}
}
public class TestDemo{
public static void main(String[] args){
//定义泛类型Test的一个Integer版本
Test<Integer> intOb = new Test<Integer>(88);
intOb.showType();
int i = intOb.getOb();
System.out.println("value= " + i);
System.out.println("--------------------------------");
//定义泛类型类Test的一个String版本
Test<String> strOb = new Test<String>("Hello Gen!");
strOb.showType();
String s = strOb.getOb();
System.out.println("value= " + s);
}
}
使用泛类型方法
public class Test{
/*
注意:定义带类型参数的方法,其主要目的是为了表达多个参数以及返回值之间的关系。
例如本例子中T和S的继承关系, 返回值的类型和第一个类型参数的值相同。
*/
public<T, S extends T> T testDemo(T t, S s){
System.out.println("我是T类型,我的类型是"+t.getClass().getName());
System.out.println("我是S类型,我的类型是"+s.getClass().getName());
return t;
}
public static void main(String[] args){
//TODO Auto-generated method stub
Test test = new Test(); //这里的Test是TestDemo中的类
Dog d = new Dog();
Animal a0 = new Animal();
Animal a1 = test.testDemo(a0,d); //加了参数,但是这个参数啥意思呢
System.out.println("我是对象 a1,我的类型是"+a1.getClass().getName());
}
}
Collection
集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大内容:对外的接口、接口的实现和对集合运算的算法。
因为集合框架中的很多类功能是相似的,所以我们用接口来规范类。Collection 接口是 Java 集合框架里的一个根接口。它也是 List、Set 和 Queue 接口的父接口。Collection 接口中定义了可用于操作 List、Set 和 Queue 的方法——增删改查。
List
List
是一个接口,不能实例化,需要一个具体类来实现实例化。
List 集合中的对象按照一定的顺序排放,里面的内容可以重复。 List 接口实现的类有:ArrayList
(实现动态数组),Vector
(实现动态数组),LinkedList
(实现链表),Stack
(实现堆栈)。
List 在 Collection 基础上增加的方法:
ArrayList
ArrayList
类实现一个可增长的动态数组,位于java.util.ArrayList
。实现了 List
接口,它可以存储不同类型的对象(包括 null
在内),而数组则只能存放特定数据类型的值。
ArrayList 编程实例
学校的教务系统会对学生进行统一的管理,每一个学生都会有一个学号和学生姓名,我们在维护整个系统的时候,大多数操作是对学生的添加、插入、删除、修改等操作。
/**
* 学生类
*/
public class Student {
public String id;
public String name;
public Student(String id, String name){
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
}
import java.util.*;
public class ListTest {
//集合后面的<>代表泛型的意思
//泛型是规定了集合元素的类型
/**
* 用于存放学生的List
*/
public List<Student> students;
public ListTest() {
this.students = new ArrayList<Student>();
}
/**
* 用于往students中添加学生
*/
public void testAdd() {
// 创建一个学生对象,并通过调用add方法,添加到学生管理List中
Student st1 = new Student("1", "张三");
students.add(st1);
// 取出 List中的Student对象 索引为0 也就是第一个
Student temp = students.get(0);
System.out.println("添加了学生:" + temp.id + ":" + temp.name);
Student st2 = new Student("2", "李四");
//添加到list中,插入到索引为0的位置,也就是第一个
students.add(0, st2);
Student temp2 = students.get(0);
System.out.println("添加了学生:" + temp2.id + ":" + temp2.name);
// 对象数组的形式添加
Student[] student = {new Student("3", "王五"), new Student("4", "马六")};
// Arrays类包含用来操作数组(比如排序和搜索)的各种方法,asList() 方法用来返回一个受指定数组支持的固定大小的列表
students.addAll(Arrays.asList(student));
Student temp3 = students.get(2);
Student temp4 = students.get(3);
System.out.println("添加了学生:" + temp3.id + ":" + temp3.name);
System.out.println("添加了学生:" + temp4.id + ":" + temp4.name);
Student[] student2 = {new Student("5", "周七"), new Student("6", "赵八")};
students.addAll(2, Arrays.asList(student2));
Student temp5 = students.get(2);
Student temp6 = students.get(3);
System.out.println("添加了学生:" + temp5.id + ":" + temp5.name);
System.out.println("添加了学生:" + temp6.id + ":" + temp6.name);
}
/**
* 取得List中的元素的方法
*/
public void testGet() {
int size = students.size();
for (int i = 0; i < size; i++) {
Student st = students.get(i);
System.out.println("学生:" + st.id + ":" + st.name);
}
}
/**
* 通过迭代器来遍历
* 迭代器的工作是遍历并选择序列中的对象,Java 中 Iterator 只能单向移动
*/
public void testIterator() {
// 通过集合的iterator方法,取得迭代器实例
Iterator<Student> it = students.iterator();
System.out.println("有如下学生(通过迭代器访问):");
while (it.hasNext()) {
Student st = it.next();
System.out.println("学生" + st.id + ":" + st.name);
}
}
/**
* 通过for each 方法访问集合元素
*
*/
public void testForEach() {
System.out.println("有如下学生(通过for each):");
for (Student obj : students) {
Student st = obj;
System.out.println("学生:" + st.id + ":" + st.name);
}
//使用java8 Steam将学生排序后输出
students.stream()//创建Stream
//通过学生id排序
.sorted(Comparator.comparing(x -> x.id))
//输出
.forEach(System.out::println);
}
/**
* 修改List中的元素
*
*/
public void testModify() {
students.set(4, new Student("3", "吴酒"));
}
/**
* 删除List中的元素
*
*/
public void testRemove() {
Student st = students.get(4);
System.out.println("我是学生:" + st.id + ":" + st.name + ",我即将被删除");
students.remove(st);
System.out.println("成功删除学生!");
testForEach();
}
public static void main(String[] args) {
ListTest lt = new ListTest();
lt.testAdd();
lt.testGet();
lt.testIterator();
lt.testModify();
lt.testForEach();
lt.testRemove();
}
}
在上面的代码中,用到了 Arrays 类, Arrays 包含用来操作数组(比如排序和搜索)的各种方法,asList() 方法用来返回一个受指定数组支持的固定大小的列表。
Map
Map 接口也是一个非常重要的集合接口,用于存储键 / 值对。Map 中的元素都是成对出现的,键值对就像数组的索引与数组的内容的关系一样,将一个键映射到一个值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。我们可以通过键去找到相应的值。
value
可以存储任意类型的对象,我们可以根据 key
键快速查找 value
。Map
中的键 / 值
对以Entry
类型的对象实例形式存在。
HashMap
HashMap
是基于哈希表的 Map
接口的一个重要实现类。HashMap
中的 Entry
对象是 无序 排列的,Key
值和 value
值都可以为 null
,但是一个 HashMap
只能有一个 key
值为 null
的映射(key
值不可重复)。
// Course.java
public class Course {
public String id;
public String name;
public Course(String id, String name){
this.id = id;
this.name = name;
}
}
// MapTest.java
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.Set;
public class MapTest {
/**
* 用来承装课程类型对象
*/
public Map<String, Course> courses;
/**
* 在构造器中初始化 courses 属性
* @param args
*/
public MapTest() {
this.courses = new HashMap<String, Course>();
}
/**
* 测试添加:输入课程 ID,判断是否被占用
* 若未被占用,输入课程名称,创建新课程对象
* 并且添加到 courses 中
* @param args
*/
public void testPut() {
//创建一个 Scanner 对象,用来获取输入的课程 ID 和名称
Scanner console = new Scanner(System.in);
for(int i = 0; i < 3; i++) {
System.out.println("请输入课程 ID:");
String ID = console.next();
//判断该 ID 是否被占用
Course cr = courses.get(ID);
if(cr == null){
//提示输入课程名称
System.out.println("请输入课程名称:");
String name = console.next();
//创建新的课程对象
Course newCourse = new Course(ID,name);
//通过调用 courses 的 put 方法,添加 ID-课程映射
courses.put(ID, newCourse);
System.out.println("成功添加课程:" + courses.get(ID).name);
}
else {
System.out.println("该课程 ID 已被占用");
continue;
}
}
}
/**
* 测试 Map 的 keySet 方法
* @param args
*/
public void testKeySet() {
//通过 keySet 方法,返回 Map 中的所有键的 Set 集合
Set<String> keySet = courses.keySet();
//遍历 keySet,取得每一个键,在调用 get 方法取得每个键对应的 value
for(String crID: keySet) {
Course cr = courses.get(crID);
if(cr != null){
System.out.println("课程:" + cr.name);
}
}
}
/**
* 测试删除 Map 中的映射
* @param args
*/
public void testRemove() {
//获取从键盘输入的待删除课程 ID 字符串
Scanner console = new Scanner(System.in);
while(true){
//提示输出待删除的课程 ID
System.out.println("请输入要删除的课程 ID!");
String ID = console.next();
//判断该 ID 是否对应的课程对象
Course cr = courses.get(ID);
if(cr == null) {
//提示输入的 ID 并不存在
System.out.println("该 ID 不存在!");
continue;
}
courses.remove(ID);
System.out.println("成功删除课程" + cr.name);
break;
}
}
/**
* 通过 entrySet 方法来遍历 Map
* @param args
*/
public void testEntrySet() {
//通过 entrySet 方法,返回 Map 中的所有键值对
Set<Entry<String,Course>> entrySet = courses.entrySet();
for(Entry<String,Course> entry: entrySet) {
System.out.println("取得键:" + entry.getKey());
System.out.println("对应的值为:" + entry.getValue().name);
}
}
/**
* 利用 put 方法修改Map 中的已有映射
* @param args
*/
public void testModify(){
//提示输入要修改的课程 ID
System.out.println("请输入要修改的课程 ID:");
//创建一个 Scanner 对象,去获取从键盘上输入的课程 ID 字符串
Scanner console = new Scanner(System.in);
while(true) {
//取得从键盘输入的课程 ID
String crID = console.next();
//从 courses 中查找该课程 ID 对应的对象
Course course = courses.get(crID);
if(course == null) {
System.out.println("该 ID 不存在!请重新输入!");
continue;
}
//提示当前对应的课程对象的名称
System.out.println("当前该课程 ID,所对应的课程为:" + course.name);
//提示输入新的课程名称,来修改已有的映射
System.out.println("请输入新的课程名称:");
String name = console.next();
Course newCourse = new Course(crID,name);
courses.put(crID, newCourse);
System.out.println("修改成功!");
break;
}
}
public static void main(String[] args) {
MapTest mt = new MapTest();
mt.testPut();
mt.testKeySet();
mt.testRemove();
mt.testModify();
mt.testEntrySet();
}
}
Set 和 HashSet
Set 接口也是 Collection 接口的子接口,它有一个很重要也是很常用的实现类——HashSet,Set 是元素无序并且不包含重复元素的 collection(List 可以重复),被称为集。
HashSet 由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。
Collections
算法