Collection集合
-
概述: 集合是java中提供的一种容器,可以用来存储多个引用数据类型的数据
-
分类:
-
单列集合: 以单个单个元素进行存储
-
双列集合: 以键值对的形式进行存储
-
-
集合与数组的区别:
-
长度:
-
数组长度是固定的
-
集合长度是不固定的
-
-
-
存储范围:
-
数组可以存储基本类型+引用类型 eg; int[],String[]
-
集合只能存储引用类型,如果要存储基本类型,需要存储基本类型对应的包装类类型 eg; ArrayList<String> ,ArrayList<Integer>
-
单列集合常用类的继承体系
-
单列集合: 以单个单个元素进行存储
-
单列集合继承体系:
-
Collection接口: 是所有单列集合的顶层父接口,所以Collection中定义了所有单列集合共有的方法
-
List接口: 继承Collection接口,特
-
特点: 元素有索引,元素可重复,元素存取有序
-
ArrayList类: 底层采用的是数组结构,查询快,增删慢
-
LinkedList类: 底层采用的是链表结构,查询慢,增删快
-
...
-
-
Set接口: 继承Collection接口,特点: 元素无索引,元素唯一(不可重复)
-
HashSet类: 底层采用的是哈希表结构,由哈希表结构保证元素唯一,元素存取无序
-
LinkedHashSet类:底层采用的是哈希表+链表结构,由哈希表结构保证元素唯一,由链表保证元素存取有序
-
TreeSet类:底层采用的是红黑树结构,可以对元素进行排序
-
-
Collection 常用功能
-
Collection是接口,只能通过其子类创建对象
-
Collection是所有单列集合的顶层父接口,所以所有单列集合都拥有Collection中的方法
-
public boolean add(E e)
: 把给定的对象添加到当前集合中 。 -
public void clear()
:清空集合中所有的元素。 -
public boolean remove(E e)
: 把给定的对象在当前集合中删除。 -
public boolean contains(Object obj)
: 判断当前集合中是否包含给定的对象。 -
public boolean isEmpty()
: 判断当前集合是否为空。 -
public int size()
: 返回集合中元素的个数。 -
public Object[] toArray()
: 把集合中的元素,存储到数组中
Iterator迭代器
迭代的概念
-
概述:迭代即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
-
迭代的步骤:
-
获取迭代器对象
-
使用迭代器对象判断集合中是否有元素可以取出
-
如果有元素可以取出,就直接取出来该元素,如果没有元素可以取出,就结束迭代
-
获取迭代器对象
-
public Iterator iterator()
: 获取集合对应的迭代器,用来遍历集合中的元素的。
Iterator迭代器对象的常用方法
-
public boolean hasNext()
:如果仍有元素可以迭代,则返回 true。 -
public E next()
:返回迭代的下一个元素。 -
void remove()
删除当前迭代出来的元素
public static void main(String[] args) { Collection<String> col = new ArrayList<>(); col.add("张三"); col.add("李四"); col.add("王五"); col.add("李里欧"); // 获取迭代器 Iterator<String> it = col.iterator(); while (it.hasNext()) { String e = it.next(); // 循环判断是否有元素可以取出来 System.out.println("取出的元素"+e); // 删除当前迭代出来的元素 it.remove(); } System.out.println("col:"+col);// col:[] }
Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素。在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。
-
概述: 增强for循环(foreach循环),是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和Collection集合
-
原理: 内部基于Iterator迭代器实现,所以在遍历的过程中,不能对集合中的元素进行增删操作,否则抛出ConcurrentModificationException并发修改异常
public static void main(String[] args) {
// 创建Collection集合对象,限制集合元素的类型为String类型
Collection<String> col = new ArrayList<>();
// 往集合中添加元素
col.add("霆锋");
col.add("宝强");
col.add("乃亮");
col.add("羽凡");
// 增强for循环
for (String name : col) {
System.out.println("name:" + name);
//col.add("粤明");// 报并发修改异常
//col.remove(name);// 报并发修改异常
}
System.out.println("--------------------");
// 创建String类型的数组,并存储元素
String[] arr = {"冠希", "志祥", "吴签", "俊杰"};
// 增强for循环
for (String name : arr) {
System.out.println("name:" + name);
}
}
-
概述: JDK5之后,新增了泛型(Generic)语法,可以在类、接口或方法中预支地使用未知的类型
-
简而言之: 泛型其实就是表示一种未知的数据类型,在使用的时候确定其具体数据类型
-
表示方式: <泛型变量> 泛型变量任意字母
-
泛型的好处:
-
将运行时期的ClassCastException,转移到了编译时期变成了编译失败
-
避免了类型转换的麻烦
-
-
案例:
-
集合不使用泛型
-
可能会发生类型转换异常
-
避免类型转换异常,就需要先做类型判断,再转型--->比较麻烦
-
-
public static void main(String[] args) {
// 创建Collection集合,不使用泛型
Collection col = new ArrayList();
// 往集合中添加元素
col.add("张三");
col.add(18);
col.add("中国北京");
col.add(3.14);
System.out.println("col:" + col);
// 循环遍历
for (Object obj : col) {
// 获取字符串元素的长度,打印输出
//String str = (String)obj;
//System.out.println("字符串元素的长度:"+str.length());
// 避免类型转换异常
if (obj instanceof String){
String str = (String)obj;
System.out.println("字符串元素的长度:"+str.length());
}
}
}
-
创建含有泛型的类的对象的时候,指定泛型的具体数据类型(只能是引用数据类型)
public static void main(String[] args) {
// 创建含有泛型的类的对象的时候,指定泛型的具体数据类型(只能是引用数据类型)
// 创建MyGenericClass对象,指定泛型的具体数据类型为String
MyGenericClass<String> mc1 = new MyGenericClass<>();
// 访问MyGenericClass类的成员变量
mc1.e = "itheima";
System.out.println("e:" + mc1.e);
// 访问MyGenericClass类的成员方法
String res1 = mc1.method("itcast");
System.out.println("res1:"+res1);
System.out.println("---------------");
// 创建MyGenericClass对象,指定泛型的具体数据类型为Integer
MyGenericClass<Integer> mc2 = new MyGenericClass<>();
// 访问MyGenericClass类的成员变量
mc2.e = 100;
System.out.println("e:" + mc2.e);
// 访问MyGenericClass类的成员方法
Integer res2 = mc2.method(200);
System.out.println("res2:"+res2);
}
实现类实现接口的时候,确定接口泛型的具体数据类型
public class 类名 implements 接口名<具体的引用数据类型>{}
常见数据结构
-
栈:stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在表的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
简单的说:采用该结构的集合,对元素的存取有如下的特点
-
先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。
栈的入口、出口的都是栈的顶端位置。
队列:queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行取出并删除。
简单的说,采用该结构的集合,对元素的存取有如下的特点:
-
先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。
-
队列的入口和出口在两侧。例如,下图中的左侧为入口,右侧为出口。
数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人
查找元素快:通过索引,可以快速访问指定位置的元素
增删元素慢
链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表,那么这里给大家介绍的是单向链表\双向链表
-
查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素。
-
增删元素快:只需要修改链接下一个元素的地址值即可
树基本结构介绍
树具有的特点
-
每一个节点有零个或者多个子节点
-
没有父节点的节点称之为根节点,一个树最多有一个根节点。
-
每一个非根节点有且只有一个父节点
二叉查找树
二叉查找树的特点:
-
左子树上所有的节点的值均小于等于他的根节点的值
-
右子树上所有的节点值均大于或者等于他的根节点的值
-
每一个子节点最多有两个子树
平衡二叉树
它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树
左旋就是将节点的右支往左拉,右子节点变成父节点,并把晋升之后多余的左子节点出让给降级节点的右子节点;
右旋: 将节点的左支往右拉,左子节点变成了父节点,并把晋升之后多余的右子节点出让给降级节点的左子节点
红黑树
红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构,它是在1972年由Rudolf Bayer发明的,当时被称之为平衡二叉B树,后来,在1978年被
Leoj.Guibas和Robert Sedgewick修改为如今的"红黑树"。它是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色,可以是红或者黑;
红黑树不是高度平衡的,它的平衡是通过"红黑树的特性"进行实现的
红黑树的特性:
-
每一个节点或是红色的,或者是黑色的。
-
根节点必须是黑色
-
每个叶节点(Nil)是黑色的;(如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点)
-
如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
-
对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;
-
List接口的概述
-
java.util.List接口继承自Collection接口,是单列集合的一个重要分支
-
-
List接口的特点
-
它是一个元素存取有序的集合
-
它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素
-
集合中可以有重复的元素
-
List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法,如下:
-
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。 -
public E get(int index)
:返回集合中指定位置的元素。 -
public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。 -
public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
List的子类
-
ArrayList集合: 底层采用的是数组结构,查询快,增删慢
-
方法: 来自Collection,List
-
-
LinkedList集合; 底层采用的是链表结构,查询慢,增删快
-
方法: 来自Collection,List,LinkedList特有的方法
-
特有的方法:
-
需求:
-
按照斗地主的规则,完成造牌洗牌发牌的动作。 具体规则:
使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
分析:
//1.造牌
//1.1 创建扑克盒单列集合,用来存储54张扑克牌
//1.2 创建花色单列集合,用来存储4个花色
//1.3 创建牌面值单列集合,用来存储13个牌面值
//1.4 往扑克盒单列集合中添加大小王
//1.5 花色和牌面值单列集合循环嵌套,拼接52张牌,添加到扑克盒集合中
//2.洗牌
//使用Collections的静态方法shuffle(List<?> list)
//3.发牌
//3.1 创建玩家1,玩家2,玩家3,底牌的集合,用来存储各自的牌
//3.2 循环遍历扑克盒中的牌
//3.3 在循环中,根据索引获取遍历出来的牌
//3.4 在循环中,判断遍历出来的牌的索引:
//3.5 如果索引>=51,牌就给底牌
//3.5 如果索引%3==0,牌就给玩家1
//3.5 如果索引%3==1,牌就给玩家2
//3.5 如果索引%3==2,牌就给玩家3
//3.6 展示牌
实现:
public static void main(String[] args) {
//1.造牌
//1.1 创建扑克盒单列集合,用来存储54张扑克牌
ArrayList<String> pokerBox = new ArrayList<>();
//1.2 创建花色单列集合,用来存储4个花色
ArrayList<String> colors = new ArrayList<>();
colors.add("♠");
colors.add("♥");
colors.add("♣");
colors.add("♦");
//1.3 创建牌面值单列集合,用来存储13个牌面值
ArrayList<String> numbers = new ArrayList<>();
for (int i = 2; i <= 10; i++) {
numbers.add(i + "");
}
numbers.add("J");
numbers.add("Q");
numbers.add("K");
numbers.add("A");
//1.4 往扑克盒单列集合中添加大小王
pokerBox.add("大王");
pokerBox.add("小王");
//1.5 花色和牌面值单列集合循环嵌套,拼接52张牌,添加到扑克盒集合中
for (String number : numbers) {
for (String color : colors) {
String pai = color + number;
// 添加到集合中
pokerBox.add(pai);
}
}
System.out.println("牌:" + pokerBox);
System.out.println("牌:" + pokerBox.size());
//2.洗牌
//使用Collections的静态方法shuffle(List<?> list)
Collections.shuffle(pokerBox);
System.out.println("打乱顺序后的牌:" + pokerBox);
System.out.println("打乱顺序后的牌:" + pokerBox.size());
//3.发牌
//3.1 创建玩家1,玩家2,玩家3,底牌的集合,用来存储各自的牌
ArrayList<String> player1 = new ArrayList<>();
ArrayList<String> player2 = new ArrayList<>();
ArrayList<String> player3 = new ArrayList<>();
ArrayList<String> diPai = new ArrayList<>();
//3.2 循环遍历扑克盒中的牌
for (int i = 0; i < pokerBox.size(); i++) {
//3.3 在循环中,根据索引获取遍历出来的牌
String pai = pokerBox.get(i);
//3.4 在循环中,判断遍历出来的牌的索引:
if (i >= 51) {
//3.5 如果索引>=51,牌就给底牌
diPai.add(pai);
} else if (i % 3 == 0) {
//3.5 如果索引%3==0,牌就给玩家1
player1.add(pai);
} else if (i % 3 == 1) {
//3.5 如果索引%3==1,牌就给玩家2
player2.add(pai);
} else {
//3.5 如果索引%3==2,牌就给玩家3
player3.add(pai);
}
}
//3.6 展示牌
System.out.println("玩家1的牌:"+player1+",牌数:"+player1.size());
System.out.println("玩家2的牌:"+player2+",牌数:"+player2.size());
System.out.println("玩家3的牌:"+player3+",牌数:"+player3.size());
System.out.println("底牌:"+diPai);
}