学习内容
- 集合和数组的区别
- Collection的常用功能
- 使用迭代器取集合元素
- 增强型for实现原理和使用
- 使用泛型定义定义集合对象
- 泛型的上下限以及写法
- 泛型通配符的使用
- 类、方法、参入参数中泛型的使用
- 数据结构
- List和Set集合方法
第一节 Collection集合
1.1 集合的概念
集合:集合是JAVA提供的一种容器,用来存储多个数据.
集合和数组的区别:
(1)数组长度是固定不变的,集合是长度可变的
(2)数组中存储的是同一种数据类型,集合中可以存储多种不同的对象.以后的开发中都用集合来存储数据
1.2 集合框架
- 概述:有多种集合,我们常用地主要有Collection(单列)和Map(双列)集合.
- Collection集合拓扑图
- Collection介绍
通过上图可以看出Collection是一个抽象类,定义了一些常用的公用方法,由List和Set接口继承,再由具体的集合类去实现.这样可以利用多态实现常用的方法如添加 查询 删除等方法.
1.3 Collection集合常用方法
public boolean add(E e)
: 把给定的对象添加到当前集合中 。public void clear()
:清空集合中所有的元素。public boolean remove(E e)
: 把给定的对象在当前集合中删除。public boolean contains(E e)
: 判断当前集合中是否包含给定的对象。public boolean isEmpty()
: 判断当前集合是否为空。public int size()
: 返回集合中元素的个数。public Object[] toArray()
: 把集合中的元素,存储到数组中。
代码举例:
//以ArrayList为例,利用多态性,调用Collection方法
public static void main(String[] args) {
Collection<String> collection = new ArrayList<String>();
collection.add("ad");
collection.add("cd");
collection.remove("ad");
boolean isContains = collection.contains("cd");
collection.clear();
int size = collection.size();
boolean isEmpty = collection.isEmpty();
Object[] objects = collection.toArray();
}
第二节 迭代器
2.1 背景
在程序开发中,我们经常需要遍历集合元素,而有的集合是无序的并且没有索引的,这样我们利用一般的for循环是无法遍历的,并且如果各集合都写自己的遍历集合方法,那开发者使用起来也很费劲,由此JAVA定义了规范的遍历集合接口称为迭代器.
2.2 迭代器常用方法:
- public Boollean hasNext():如果有元素返回True
- public E next();返回下一个集合元素.
2.3 代码实现
public static void main(String[] args) {
Collection<String> collection = new ArrayList<>();
collection.add("ab");
collection.add("cd");
//得到迭代器对象
Iterator<String> iterator = collection.iterator();
//通过迭代器遍历
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
2.4 for循环的增强型
- 概述:JDK1.5以后用来专门遍历数组和集合的.实现原理其实是调用的上述的迭代器,使用起来比上述简单多了,但是不能进行增删改.因为迭代器实现原理没有索引.
- 格式:for(集合类型 变量名:集合对象)
- 代码实现
public static void main(String[] args) { Collection<String> collection = new ArrayList<>(); collection.add("ab"); collection.add("cd"); //增强型for for (String it:collection){ System.out.println(it); }
第三节 泛型
3.1 泛型概述
为了解决在未知类型的情况下传入参数的类型问题,例如集合元素可以是任何数据类型,我们可以把数据类型当做参数传进去,这样我们实例化集合的时候,传什么类型进去集合就是什么类型.如果不传类型进行,java则把集合类型当成Object,这样每次必须经过强转.
3.2 泛型的使用
3.2.1 泛型在类中的使用
- 格式:
class 类名<泛型的变量> { ... }
- 代码举例:
public static void main(String[] args) {
//实例化的时候确定传入类型String
User<String> user = new User<>();
String name = "张三";
user.name = name;
}
//属性name引用T泛型
public static class User<T>{
public T name;
}
3.2.2 泛型在方法中的使用
-
格式
修饰符 <泛型的变量> 返回值 方法名(参数){ }
-
代码举例
public static void main(String[] args) { User user = new User(); String name = "张三"; user.show(name); } public static class User{ //T名可以随意起,多个点话用逗号隔开 public <T> void show(T name){ System.out.println("类名:" + name.getClass()); System.out.println("值:"+name); } }
3.2.3 泛型在接口中的使用
- 格式
interface 接口名<泛型名>{ .... }
- 代码实现
接口代码
public interface FanInteface<V> {
public <V> void show(V name);
}
- 接口实现代码1
public class FanImpl<V> implements FanInteface<V> {
@Override
public <V> void show(V name) {
....
}
}
- 接口实现代码2
public class FanImpl implements FanInteface<String> {
@Override
public <String> void show(String name) {
}
}
3.2.4 泛型的通配符
- 概述:当我们调用泛型类或者泛型接口的时候,我们无法确定传入的类型是什么,可以使用泛型通配符?表示
- 泛型的上限
格式: 类型名称 <? extends 类 > 对象名称
意义: 只能接收该类型及其子类 - 泛型的下限
格式: 类型名称 <? super 类 > 对象名称
意义: 只能接收该类型及其父类型 - 代码实现
public static void main(String[] args) {
ArrayList<Integer> num1 = new ArrayList<>();
ArrayList<String> num2 = new ArrayList<>();
num1.add(1);
num2.add("张三");
getElement(num1);//运行正常
getElement(num2);//运行正常
getElement1(num1);//不符合泛型限定范围 报错
getElement2(num1);//运行正常
getElement2(num2);//报错:不符合泛型限定范围
}
//泛型没有限定范围,所有类型都接受.
public static void getElement(Collection<?> collection){
System.out.println(collection);
}
//必须是Number类型或者Number类型的父类
public static void getElement1(Collection<? super Number> collection){
System.out.println(collection);
}
//必须是Number类型或者Number类型的子类
public static void getElement2(Collection<? extends Number> collection){
System.out.println(collection);
}
第四节 数据结构
4.1 结构概述
集合在内存中存储的结构常用的有栈、队列、数组、链表、红黑树.
栈:先进后出
队列:先进先出
数组:长度不可变,地址是连续的,查询快,修改每次都要新增一整块地址,因此修改慢
链表:由一系列点组成,每个点记录着数据和下个地址,地址不是连续的,查找的时候要一个一个找,修改的时候只需要修改数据即可,因此查询慢修改快.
红黑树:二叉树(叶子节点最多有俩个),平横数(左右子树相等),不平衡数(左右子数不相等),红黑树是介于平衡和不平衡数之间的.
第五节 List集合
5.1 List特点
- 元素有序
- 带有索引
- 允许有重复值
5.2 List的常用方法
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("ab");//添加
list.add(1,"cd");
list.add(2,"ef");
int cd = list.indexOf("ef");
System.out.println("索引号:"+cd);
boolean empty = list.isEmpty();
System.out.println("集合是否为空:"+empty);
list.remove(0);//根据索引移除
String st = list.get(1);//获取索引为1的值
for (String s : list) {
System.out.println("遍历集合:" +s);
}
5.3 List的子类(ArrayList 和LinkedList)
5.3.1 ArrayList 子类
- 特点:继承List集合的所有方法,原理由数组组成,查询快,增删慢,有索引,可以有重复值,连续.
5.3.2 LinkedList
- 特点:继承List的所有方法,原理是双向链表形式,查询慢,增删快.双向链表每个子节点除了记录数据值和下一个地址,还记录了上一个地址值.
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("ab");
linkedList.add(1, "cd");
linkedList.add(2, "ef");
linkedList.get(2);//根据索引获取值
linkedList.addLast("hg");//向尾部添加字符
linkedList.addFirst("oo");//向首部添加
linkedList.removeFirst();
linkedList.removeLast();
String pop = linkedList.pop();//堆栈弹出一个元素,相当于removeFirst()
linkedList.push("xx");//将元素推入堆栈,相当于向首部添加元素
System.out.println(linkedList.getLast());//打印出尾部
System.out.println("遍历结合------");
for (String s : linkedList) {
System.out.println(s);
}
}
第六节 Set集合
- 特点:Set结合元素无序,并且以某种规则保证无重复值
6.1 HashSet集合
- 概述:根据哈希值来确定存储的位置,保证元素的唯一性.依赖于hashCode和equals方法,可以重写这俩方法
- 哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的
- 自定义引用类型HashSet集合的时候,需要我们重写hashcode和equals方法,equals返回的值如果是true,那这两个对象的hash值就相等.
- 代码举例
public class Student {
private String name;
private int age;
public Student(){};
public Student(String name, int age){
this.setName(name);
this.setAge(age);
}
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;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o == null || o.getClass() != this.getClass()) {
return false;
}
Student student = (Student) o;
return (student.getAge() == age && Objects.equals(student.getName(), name));
}
//根据姓名和年龄获取哈希值
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
代码实现
public static void main(String[] args) {
HashSet<Student> students = new HashSet<>();
Student stu1 = new Student("张三",10);
Student stu2 = new Student("李四",20);
Student stu3 = new Student("张三",10);
students.add(stu1);
students.add(stu2);
students.add(stu3);
for (Student student : students) {
//打印出来的顺序是不一定的
System.out.println(student.toString());
}
}
6.2 LinkedHashSet
-
概述:相比HashSet而言LinkedHashSet是有顺序的.按照插入的顺序而定
-
代码演示
LinkedHashSet<String> str = new LinkedHashSet<>(); str.add("aaaa"); str.add("ccc"); str.add("bbb"); for (String s : str) { //打印的结果必须 aaa ccc bbb System.out.println(s); }
6.3 可变参数
-
格式 返回值 方法名(参数类型… 参数名){ }
-
int… 和int[] 的区别
int[]作为参数类型传的时候传入必须是数组类型
int…作为参数类型的时候可以是多个此类型的数据.
public class demo2 {
public static void main(String[] args) {
int sum = sum(10, 10, 20, 30);
System.out.println("求和的结果"+sum);
}
public static int sum(int... i){
for (int num : i) {
return num++;
}
return 0;
}
}