集合
集合
第一章 Collection集合(单列集合)
1.1集合概述
- 集合:集合是Java中提供的一种容器,可以用来存储多个数据。
集合和数组的区别:
1.数组的长度是固定的。
集合的长度是可变的。
2.数组中存储的是同一种元素,可以存储基本类型数据值。
集合存储的都是对象,而且对象的类型可以不一致,在开发中一般当对象多的时候,使用集合进行存储。
1.2集合框架
集合按照其存储结构可以分为两大类:分别是==单列集合==(java.util.Collection)和==双列集合==(java.util.Map)
- Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是java.util.List和java.util.Set,其中List的特点是元素有序、元素可重复。Set的特点是元素无序,而且不可重复。List接口的主要实现类有java.util.ArrayList和java.util.LinkedList,Set接口的主要实现类有java.util.HashSet和java.util.TreeSet
- 橙色框里填写的都是接口类型,而蓝色框里填写的都是具体的实现类
- 集合本身是一个工具,它存放在java.util包中。在Collection接口定义着单列集合框架中最最共性的内容
1.3Collection常用功能
Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。
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():把集合中的元素,存储到数组中
package Rosyday01; import java.util.ArrayList; import java.util.Collection; public class Rosy10Collection { public static void main(String[] args) { Collection<String> coll=new ArrayList<>(); System.out.println(coll);//重写了toString方法 [] /* 1.public boolean add(E e):把给定的对象添加到当前集合中 */ boolean b1=coll.add("张三"); coll.add("李四"); coll.add("王五"); coll.add("赵六"); coll.add("田七"); coll.add("孙八"); System.out.println("b1:"+b1);//b1:true System.out.println(coll);//[张三, 李四, 王五, 赵六, 田七, 孙八] /* 2.public boolean remove(E e):把给定的对象在当前集合中删除 */ boolean b2=coll.remove("张三"); System.out.println("b2:"+b2);//b2:true System.out.println(coll);//[李四, 王五, 赵六, 田七, 孙八] /* 3.public boolean contains(E e):判断当前集合中是否包含给定的对象 */ boolean b3=coll.contains("赵六"); System.out.println("b3:"+b3);//b3:true /* 4.public int size():返回集合中元素的个数 */ int b4=coll.size(); System.out.println("集合中元素的个数是:"+b4);//集合中元素的个数是:5 /* 5.public boolean isEmpty():判断当前集合是否为空 */ boolean b5=coll.isEmpty(); System.out.println("b5:"+b5);//b5:false /* 6.public object[ ] toArray():把集合中的元素,存储到数组中 */ Object[] arr = coll.toArray(); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } /* 7.public void clear():清空集合中所有的元素,但集合还在 */ coll.clear(); System.out.println(coll);//[] } }
第二章 Iterator迭代器
2.1Iterator接口
在程序开发中,要遍历集合中的所有元素,jdk专门提供了一个接口java.util,Iterator。Colection和Map接口主要用于存储数据,而Iterator接口主要用于迭代访问(即遍历)Colection中的元素,因此Iterator对象也被称作迭代器。
1.获取迭代器的方法:
**public Iterator iterator( )**:获取集合对应的迭代器,用于遍历集合中的元素
2.迭代的概念:
迭代:即Collection集合元素的通用获取方法。在取元素之前要判断集合中有没有元素,如果有就把这个元素取出来,继续再判断,如果还有就再取出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
3.Iterator接口的常用方法如下:
- **public E next( )**:返回迭代的下一个元素
- **public boolean hasNext( )**:如果仍有元素可以迭代,则返回true
4.迭代器的使用步骤(==重点==):
1.使用集合中的方法iterator( )获取迭代器的实现类对象,使用Iterator接口接收(多态)
2.使用Iterator接口中的方法hasNext判断还有没有下一个元素
3.使用Iterator接口中的方法next取出集合中的下一个元素
package Demo02;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo01Iterator {
public static void main(String[] args) {
Collection<String>coll=new ArrayList<>();//使用多态方式创建对象
//添加元素到集合
coll.add("叽叽叽");
coll.add("哈哈哈");
coll.add("啦啦啦");
coll.add("略略略");
coll.add("啧啧啧");
coll.add("呵呵呵");
Iterator<String> iterator=coll.iterator();
// 1.使用集合中的方法iterator( )获取迭代器的实现类对象,使用Iterator接口接收(多态)
//注意:Iterator<E>接口也是有泛型的,泛型的类型跟集合泛型的类型保持一致
while (iterator.hasNext()){//2.使用Iterator接口中的方法hasNext判断还有没有下一个元素
System.out.println(iterator.next());//3.使用Iterator接口中的方法next取出集合中的下一个元素
}
}
}
2.2迭代器的实现原理
java.util.Iterator接口:迭代器(对集合进行遍历)
1.boolean hasNext() 如果仍有元素可以迭代,则返回true,没有返回false
2.E next() 返回迭代的下一个元素
取出集合中的下一个元素
Iterator迭代器,是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方法比较特殊
Collection接口中有一个方法,叫iterator(),这个方法返回的就是迭代器的实现类对象
Iteratoriterator() 返回在此 collection的元素上进行迭代的迭代器 当没有元素可取时,iterator.hasNext()返回值为false,再输出iterator.next()时程序会报错,显示Exception in thread “main” java.util.NoSuchElementException,==NoSuchElementException没有元素异常==
Iterator
iterator===coll.iterator();==高亮部分是实现类对象,获取迭代器的实现类对象,并把指针(索引)指向集合的-1索引 iterator.next() 1.取出下一个元素
2.会把指针向后移动一位
2.3增强for
增强for循环(也称for each循环)jdk1.5以后,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历过程中,不能对集合中的元素进行增删操作。
for(集合/数组元素的数据类型 变量名:集合名/数组名){
//写操作代码
System.out.println(变量名);
}
底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写
**Collection
extends Iterable **:所有单列集合都可以使用增强for **public interface Iterable
**实现了这个接口,允许对象成为”foreach”语句的目标
例一:遍历数组
package Demo02;
public class Demo01foreach {
public static void main(String[] args) {
int []arr={1,4,6,8,5};
//使用增强for遍历数组
for (int a:arr) {//a代表数组中的每个元素
System.out.println(a);
}
}
}
例二:遍历集合
package Demo02;
import java.util.ArrayList;
import java.util.Collection;
public class Demo01foreach {
public static void main(String[] args) {
Collection<String> coll=new ArrayList<>();
coll.add("科比");
coll.add("艾弗森");
coll.add("奥尼尔");
coll.add("姚明");
coll.add("乔丹");
//使用增强for遍历
for (String a:coll) {//接收变量a代表 代表被遍历到的集合元素
System.out.println(a);
}
}
}
tips:增强for循环必须有遍历的目标。目标只能是Collection集合或者数组。增强for循环仅仅作为遍历出现
第三章 泛型
3.1泛型概述
集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升为Object类型,当我们在取出每一个对象,并进行相应的操作,这时必须采用类型转换。
3.2使用泛型的好处
1.创建集合对象,不使用泛型
- 好处:集合不使用泛型,默认类型就是Object类型,可以存储任意类型的数据
- 弊端:不安全,会引发异常
观察以下代码(==创建集合对象,不使用泛型==):
public class GenericDemo{
public static void main(String []args){
Collection coll=new ArrayList();
coll.add("abc");
coll.add("itcast");
coll.add(5);//由于集合没有做任何限定,任何类型都可以在此存放
Iterator it=coll.iterator();
while(it.hasNext){
//需要打印每个字符串的长度,使用String类特有方法length获取字符串长度,就要把迭代出的对象转成String类型即向下转型
String str=(String)it.next();
System.out.println(str.length());//会抛出ClassCastException类型转换异常,不能把Integer类型转换成String类型
}
}
}
5(Integer类型)无法转化成String(字符串类型)
2.==创建集合对象,使用泛型==
好处:1.避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
2.把运行期异常(代码运行之后会抛出的异常),提升到了编译期异常(写代码的时候会报错)
弊端:泛型是什么类型,只能存储什么类型的数据
package Example;
import java.util.ArrayList;
import java.util.Iterator;
class Text{
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
list.add("abb");
list.add("cdd");
list.add("ddd");
Iterator<String> iterator=list.iterator();
while (iterator.hasNext()){
String s=iterator.next();//使用泛型,不需要进行类型转换
System.out.println(s.length());
}
}
tips:泛型是数据类型的一部分,我们将类名与泛型合并一起看作数据类型。
3.3泛型的定义与使用
泛型,用来灵活地将数据类型应用到不同的类,方法,接口当中。将数据类型作为参数进行传递。
定义和使用含有泛型的类
定义格式:
修饰符 class 类名<代表泛型的变量> { }
例如,API中的ArrayList集合:
class ArrayList<E>{
pubilc boolean add(E e){ }
public E get(int index){ }
....
}
使用泛型:即什么时候确定泛型。
在创建对象的时候确定泛型
package Demo02;
public class Demo02Generic {
public static void main(String[] args) {
//不写泛型默认Object类型
Name a1=new Name();
a1.setName(1);
System.out.println(a1.getName());
//创建Name对象,泛型使用Integer类型
Name<Integer> a2=new Name<>();//Integer类型
a2.setName(331);
System.out.println(a2.getName());
//创建Name对象,泛型使用String类型
Name<String> a3=new Name<>();//字符串类型
a3.setName("小明");
System.out.println(a3.getName());
}
}
class Name<E>{
private E name;
public E getName(){
return name;
}
public void setName(E name){
this.name=name;
}
}
含有泛型的方法
定义格式:
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
例如:
public class MyGenericMethod{
public <MVP> void show1(MVP mvp){
System.out.println(mvp.getClass());
}
public <MVP> void show2(MVP mvp){
return mvp;
}
}
使用格式:调用方法时,确定泛型的类型
package Demo02;
public class Demo02GenericMethod {
public static void main(String[] args) {
GenericMethod it=new GenericMethod();
it.method01(331);
it.method01("aaa");
it.method01(8.8);
it.method01(true);
System.out.println("----------------------------------");
it.method02("静态方法不建议创建对象使用");
GenericMethod.method02(331);
GenericMethod.method02("aaa");
GenericMethod.method02("8.8");
GenericMethod.method02(true);
}
}
class GenericMethod{
//定义一个含有泛型的方法
public <M> void method01(M m){
System.out.println(m);
}
//定义一个含有泛型的静态方法
public static <S> void method02(S s){
System.out.println(s);
}
}
含有泛型的接口
定义格式:
修饰符 interface接口名<代表泛型的变量> { }
例如:
public interface MyGenericInterface<E>{
public abstract void add(E e);
public abstract E getE();
}
使用格式:
1.定义类时确定泛型的类型
2.接口使用什么泛型实现类就使用什么泛型
例如
package Demo02;
/*
测试含有泛型的接口
*/
public class Demo06GenericInterface {
public static void main(String[] args) {
//创建GenericInterfaceImpl1对象
GenericInterfaceImpl1 it1=new GenericInterfaceImpl1();
it1.method("sss");
//创建GenericInterfaceImpl2对象
GenericInterfaceImpl2<Integer> it2=new GenericInterfaceImpl2<>();
it2.method(331);
GenericInterfaceImpl2<Double> it3=new GenericInterfaceImpl2<>();
it3.method(8.8);
GenericInterfaceImpl2<Boolean> it4=new GenericInterfaceImpl2<>();
it4.method(true);
GenericInterfaceImpl2<String> it5=new GenericInterfaceImpl2<>();
it5.method("sss");
}
}
interface GenericInterface<I>{
public abstract void method(I i);
}
/*
含有泛型的接口,第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型
public interface Iterator<E>{
E next();
}
Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写的next方法泛型默认就是String
public final class Scanner implements Iterator<String>{
public String next() { }
}
*/
class GenericInterfaceImpl1 implements GenericInterface<String>{
@Override
public void method(String s) {
System.out.println(s);
}
}
/*
含有泛型的接口,第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走
就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型
public interface list(E){
boolean add(E e);
E get(int index);
}
public class ArrayList<E> implements List<E>{
public boolean add(E e) { }
public E get(int index) { }
}
*/
class GenericInterfaceImpl2<I> implements GenericInterface<I> {
@Override
public void method(I i) {
System.out.println(i);
}
}
3.4泛型通配符
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示,但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
通配符基本使用
泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。?:代表任意的数据类型
此时只能接受数据,不能往该集合中存储数据。
- 使用方式:不能创建对象使用,只能作为方法的参数使用
tips:泛型是没有继承概念的
package Demo02;
import java.util.ArrayList;
import java.util.Iterator;
public class Demo07Generic {
public static void main(String[] args){
ArrayList<Integer> a1=new ArrayList<>();
a1.add(3);
a1.add(2);
ArrayList<String> a2=new ArrayList<>();
a2.add("a");
a2.add("b");
printArray(a1);
printArray(a2);
}
public static void printArray(ArrayList<?> list){//不知道集合里存储的是什么类型的数据时,我们使用泛型的通配符来接受数据
Iterator<?> iterator=list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
通配符高级使用—受限泛型
在java的泛型中可以指定一个泛型的上限和下限
泛型的上限:
- 格式:类型名称 <? extends 类> 对象名称
- 意义:只能接收该类型及其子类
泛型的下限:
- 格式:类型名称 <? super类> 对象名称
- 意义:只能接收该类型及其父类
比如:现已知Object类,String类,Number类,Integer类,Number是Integer的父类
package Demo02;
import java.util.ArrayList;
import java.util.Collection;
public class Demo08Generic {
public static void main(String[] args) {
Collection<Integer> list1=new ArrayList<>();
Collection<String> list2=new ArrayList<>();
Collection<Number> list3=new ArrayList<>();
Collection<Object> list4=new ArrayList<>();
getElement1(list1);
getElement1(list2);//报错
getElement1(list3);
getElement1(list4);//报错
getElement2(list1);//报错
getElement2(list2);//报错
getElement2(list3);
getElement2(list4);
}
public static void getElement1 (Collection<? extends Number> cool){ }
//泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement2 (Collection<? super Number> cool){ }
//泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
}
类的继承关系:
Integer extends Number extends Object
String extends Object
第四章 集合综合案例
4.1案例介绍
按照斗地主的规则,完成发牌洗牌的动作。
具体规则:
使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
4.2案例分析
准备牌:
牌可以设计为一个ArrayList
,每个字符串为一张牌。 每张牌由花色数字两部分组成,我们可以使用花色集合和数字集合嵌套迭代完成每张牌的组装。
牌由Collection类的shuffle方法进行随机排序。
使用集合工具类Collections的方法
static void shuffle(List<?> list)使用指定的随机源对指定列表进行置换。
会随机的打乱集合中元素的顺序
发牌:
将每个人以及底牌设计为ArrayList
,将最后3张牌直接存放于底牌,剩余牌通过对3取模一次发牌。 看牌:直接打印每一个集合。
代码实现:
package Demo02;
import java.util.ArrayList;
import java.util.Collections;
public class Demo09Game {
public static void main(String[] args) {
ArrayList<String> poker=new ArrayList<>();
String[] colors={"♠","♥","♣","♦" };
String[] numbers={"2","A","K","Q","J","10","9","8","7","6","5","4","3"};
poker.add("大王");
poker.add("小王");
for (String color:colors) {
for (String number:numbers) {
poker.add(color+number);
}
}
Collections.shuffle(poker);
ArrayList<String> player01=new ArrayList<>();
ArrayList<String> player02=new ArrayList<>();
ArrayList<String> player03=new ArrayList<>();
ArrayList<String> dipai=new ArrayList<>();
for (int i = 0; i <poker.size() ; i++) {
String s = poker.get(i);
if(i>=51){
dipai.add(s);
}
else if(i%3==0){
player01.add(s);
}
else if (i%3==1){
player02.add(s);
}
else if (i%3==2){
player03.add(s);
}
}
System.out.println("玩家一"+player01);
System.out.println("玩家二"+player02);
System.out.println("玩家三"+player03);
System.out.println("底牌"+dipai);
}
}
第五章数据结构
5.1常见的数据结构
1.栈
- 栈:stack,又称堆栈,它是运算受限的线性表,其限制是允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
简单的说:采用该结构的集合,对元素的存取有如下的特点
- ==先进后出==(即,存进去的元素,要在它后面的元素依次取出后,才能取出该元素)。
- 栈的入口、出口的都是栈的顶端位置。
2.队列
- 队列:queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。
简单的说,采用该结构的集合,对元素的存取有如下特点:
- 先进先出:(即,存进去的元素,要在它前面的元素依次取出后,才能取出该元素)。例如:火车进山洞
- 队列的入口、出口各站一侧。如下图:左侧为入口,右侧为出口。
3.数组
数组:Array:是有序的元素序列,数组是在内存中开辟一段连续的空间,并在空间存放元素。就像酒店,有100个房间,从001到100每个房间都有固定编号,通过编号就可以迅速找到住在房间里的人。
特点:1.查询快:数组的地址是连续的,我们通过数组的首地址可以找到数组,通过数组的索引可以快速查找某一元素
2.增删慢:数组的长度是固定的,我们想要增加/删除一个元素,必须创建一个新数组,把源数组的数据复制过来
查找元素快:通过索引,可以快速访问指定位置的元素
增删元素慢
指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把元素组元素根据索引,复制到新数组对应索引的位置。
4.链表
- 链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时i动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表,这里记录的是单向链表。
简单的说,采用该结构的集合,对元素的存取有如下特点:
- 多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下一个人的左手,以此类推,这样多个人就在一起了。
查找元素慢:想查找某个元素,需要通过连接的结点,依次向后查找指定元素,链表中地址不是连续的,每次查询元素,都必须从头开始查询
增删元素快:链表结构,增加/删除一个元素,对链表的整体结构没有影响,所以增删快
5.红黑树
- 二叉树:binary tree,是每个结点不超过二的有序**树(tree)**。
简单的理解,就是一种类似于我们生活中的树的结构,只不过每个结点上都最多只能有两个子结点。
二叉树是每个结点最多有两个子树的树结构。顶上:根节点,两侧:“左子树”,“右子树”。
排序树/查找树
在二叉树的基础上,元素是有大小顺序的,左子树小,右子树大。
- 平衡树
- 不平衡树
红黑树
特点:趋近于平衡树,查询的速度非常的快,查询叶子节点最大次数和最小次数不能超过二倍(找到一个元素的次数不能是其他的二倍)
性质1:每个节点要么是黑色,要么是红色。
性质2:根节点是黑色。
性质3:每个叶子节点(NIL)是黑色。
性质4:每个红色结点的两个子结点一定都是黑色。
性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。
性质5.1:如果一个结点存在黑子结点,那么该结点肯定有两个子结点
第六章 List集合
6.1List接口介绍
java.util.List接口继承自Collection接口,是单列集合的重要分支,实现了List接口的对象称为List集合。在List集合中允许出现重复的元素,所有元素以一种线性方式进行存储,在程序中可以通过索引来访问集合中的指定元素。List集合的特点:元素有序,即元素的存入顺序和取出顺序一致。
List接口的特点:
1.元素存取有序的集合。(存储123,取出123)
2.带索引的集合,通过索引就可以精确的操作集合中的元素。
3.集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
6.2List接口中常用方法
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的子类
7.1ArrayList集合
对象数组 :直接打印得到的是null
数组里放的是对象的地址值。
缺点:一旦创建,程序运行期间数组的长度不可以改变。(为了解决这个问题,我们推出了ArralyList 即长度可变的数组)
ArralyList集合的概述和基本使用
1.对于ArrayList集合来说,有一个尖括号
2.泛型:也就是装在集合当中的所有元素,全都是统一的什么类型。
3.注意:泛型只能是引用类型,不能是基本类型。
- 对于ArrayList集合来说,直接打印得到的不是地址值而是内容。
如果内容是空,得到的是空的中括号[ ]
package Demo;
import java.util.ArrayList;
public class Deom02ArrayList {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();//创建一个集合,集合里元素的数据类型是String类型
// System.out.println(list);//[]
//如何向集合当中添加数据
list.add("西瓜");
System.out.println(list);
}
}
ArralyList集合的常用方法
public boolean add(E e):向集合当中添加元素,参数的类型和泛型一致。返回值代表添加是否成功。
备注:对于ArralyList集合来说add动作一定会成功,但是对于其他集合来说add动作不一定成功。
public E get(int index):从集合当中获取元素,参数是索引编号,返回值就是对应位置的元素。
public E remove(int index):从集合当中删除元素,参数是索引编号,返回值就是被删除掉的元素。
public int size():获取集合的尺寸长度,返回值是集合当中包含的元素个数。
package Rosy; import java.util.ArrayList; public class Rosy03ArrayListMethod { public static void main(String[] args) { ArrayList<String> list=new ArrayList<>(); //向集合中添加元素 add 是有返回值的 boolean success=list.add("西瓜");//返回值代表添加是否成功 System.out.println(list); System.out.println(success);//ture list.add("苹果"); list.add("芒果"); list.add("火龙果"); list.add("柚子"); list.add("哈密瓜"); System.out.println(list);//[西瓜, 苹果, 芒果, 火龙果, 柚子, 哈密瓜] //从集合中获取元素 get 索引值从0开始 String fruit=list.get(2); System.out.println("第二号索引位置:"+fruit); //从集合中删除元素 remove 索引值从0开始 String whoremoved=list.remove(3); System.out.println("被删除的元素为:"+whoremoved);//火龙果 System.out.println(list);//[西瓜, 苹果, 芒果, 柚子, 哈密瓜] //获取集合的长度尺寸,也就是其中元素的个数 size int size=list.size(); System.out.println("此时集合的长度为:"+size); } }
遍历集合
==ArralyListEach==
package Rosy;
import java.util.ArrayList;
import java.util.Iterator;
public class RosyArrayListEach {
public static void main(String[] args) {
ArrayList<String>list=new ArrayList<>();
list.add("苹果");
list.add("火龙果");
list.add("芒果");
list.add("哈密瓜");
list.add("木瓜");
//for循环
for (int i=0;i< list.size();i++){
System.out.println(list.get(i));
}
//增强for循环
for(String str : list){
System.out.println(str);
}
//迭代器
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
ArrayList集合存储基本数据
- 如果希望向集合ArrayList中存储基本数据类型,必须使用基本类型对应的“包装类”。
基本类型 包装类(引用类型,包装类都位于java.lang包下)
byte Byte
short Short
int Integer 【特殊】
long Long
float Float
double Double
char Character 【特殊】
boolean Boolean
package Rosy;
import java.util.ArrayList;
import java.util.Iterator;
public class RosyArrayListBasic {
public static void main(String[] args) {
ArrayList<Integer> listA=new ArrayList<>();
listA.add(3);
listA.add(8);
listA.add(7);
Iterator<Integer>iterator=listA.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
==从jdk1.5开始,支持自动装箱拆箱==
自动装箱
基本数据类型自动转换为包装类型
自动拆箱
包装数据类型自动转换为基本类型
例一:生成六个1~33之间的随机数,并放在集合中,且遍历集合
package Example;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
class Example1{
public static void main(String[] args) {
Random random=new Random();
ArrayList<Integer>list=new ArrayList<>();
for (int i = 0; i < 6; i++) {
int r=random.nextInt(33)+1;
list.add(r);
}
Iterator<Integer>iterator=list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
7.2LinkedList集合
java.util.LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。
java.util.LinkedList集合 implement List接口
LinkedList集合的特点:
底层是一个双向链表结构,查询慢,增删快
里面包含了大量操作首尾元素的方法
注意:使用LinkedList集合特有的方法,不能使用多态
LinkedList是一个双向链表
实际开发中对一个集合元素的增添和删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法
public void addFirst(E e)====push()压栈
:将指定元素插入此列表的开头。public void addLast(E e)
:将指定元素添加到此列表的结尾。getFirst() getLast()
:返回此列表的第一个元素/返回此列表的最后一个元素removeFirst()==pop() 弹栈 removeLast()
:移除并返回此列表的第一个元素/移除并返回此列表的最后一个元素
代码如下:
package Demo02;
import java.util.Iterator;
import java.util.LinkedList;
public class Demo10LinkedList {
public static void main(String[] args) {
LinkedList<String> list01=new LinkedList<>();
list01.add("小明");
list01.add("小华");
list01.add("小王");
System.out.println(list01);
/*
想添加【小金】到表头
使用addFirst();或push();方法
*/
// list01.addFirst("小金");
list01.push("小金");//进栈
Iterator<String> iterator01=list01.iterator();
while (iterator01.hasNext()){
System.out.println(iterator01.next());
}
System.out.println(list01);
/*
想添加【小许】到表尾
使用addLast();方法
*/
list01.addLast("小许");
Iterator<String> iterator02=list01.iterator();
while (iterator02.hasNext()){
System.out.println(iterator02.next());
}
System.out.println(list01);
/*
返回列表的第一个和最后一个元素 getFirst();和getLast()方法
*/
System.out.println(list01.getFirst());
System.out.println(list01.getLast());
/*
移除列表的第一个元素
使用removeFirst();或pop();方法 弹栈
*/
// list01.removeFirst();
list01.pop();
System.out.println(list01);
/*
移除列表的最后一个元素
使用removeLast();方法
*/
list01.removeLast();
System.out.println(list01);
}
}
7.3Vector集合
Vector
类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector
的大小可以根据需要增大或缩小,以适应创建 Vector
后进行添加或移除项的操作。(了解即可) 单线程,速度慢
第八章 Set集合
java.util.Set
接口和java.util.List
接口一样,同样继承自Collection
接口,它与Collection
接口中的方法基本一致,并没有对Collection
接口进行功能上的扩充,只是比Collection
接口更加严格了。与List
接口不同的是,Set
接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
Set
集合有多个子类,我们介绍的是java.util.HashSet
和java.util.LinkedHashSet
这两个集合
tips:Set集合取出元素的方式可以采用:迭代器、增强for。
8.1HashSet集合
java.util.HashSet
是Set
接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。java.util.HashSet
底层其实是一个java.util.HashMap
支持。
HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此和具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCode
和equals
方法
特点:
- 不允许存储重复的元素
- 没有索引,没有带索引的方法,也不能使用普通的for循环遍历
- 是一个无序的集合,存储元素和取出元素的顺序有可能不一致
- 底层是一个哈希表结构(查询的速度非常的快)
package Demo02;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Demo11Set {
public static void main(String[] args) {
Set<Integer> set=new HashSet<>();
set.add(1);
set.add(3);
set.add(2);
set.add(1);
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());//1,2,3
}
System.out.println("----------------------------------------");
for (Integer a:set) {
System.out.println(a);
}
}
}
8.2哈希表
HashSet集合存储数据的结构
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过Key值依次查找的效率较低。
在JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查询时间。
哈希值(Hash Code):是一个十进制的整数,由系统随即给出(就是对象的地址值,是一个逻辑地址,是模拟出来的,不是数据实际存储的物理地址)
在Object类有一个hashCode();
方法,返回该对象的哈希码值。
源码:public native int hashCode( ); native:代表该方法调用的是本地操作系统的方法
tips:toString方法返回的就是哈希值的十六进制值,哈希值和toString返回的值,都不是真正物理意义上的地址值,是模拟的假的地址值
package Demo02;
public class Demo12HashCode {
public static void main(String[] args) {
persion p1=new persion();
persion p2=new persion();
int code1 = p1.hashCode();
int code2 = p2.hashCode();
System.out.println(code1);//重写前:460141958 重写后:1
System.out.println("--------------------------");
System.out.println(code2);//重写前:1163157884 重写后:1
System.out.println(p1);//重写前:Demo02.persion@1b6d3586 重写后:Demo02.persion@1
System.out.println(p2);//重写前:Demo02.persion@4554617c 重写后:Demo02.persion@1
System.out.println(p1==p2);//false 即真正意义上的地址值不相等
}
}
/*
1b6d3586(十六进制)--->(十进制)460141958
4554617c(十六进制)--->(十进制)1163157884
*/
class persion extends Object{
@Override
public int hashCode() {
// return super.hashCode();源码
return 1;//重写hashCode方法
}
}
tips:String类里重写了hashCode方法
hashCode
public int hashCode()
返回此字符串的哈希码。String 对象的哈希码根据以下公式计算:
**s[0]31^(n-1) + s[1]31^(n-2) + … + s[n-1]
使用 int 算法,这里 s[i] 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。(空字符串的哈希值为 0。)
哈希冲突
两个元素不同,但是哈希值相同
数组超过8位
少6变回数组
解决办法:
- 拉链法
- 开放寻址
8.3Set集合存储元素不重复原理
哈希表中,没有重复元素的原因:集合在调用add方法时,add方法会调用元素的hashCode方法和equals方法。
package Demo02;
import java.util.HashSet;
import java.util.Set;
public class Demo13HashSetSaveString {
public static void main(String[] args) {
Set<String> set=new HashSet<>();
String s1=new String("abc");
String s2=new String("abc");
set.add(s1);
set.add(s2);
set.add("重地");
set.add("通话");
set.add("abc");
System.out.println(set);//[重地, 通话, abc]
}
}
8.4HashSet存储自定义类型元素
给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一。
package Demo02;
import java.util.HashSet;
import java.util.Objects;
public class Demo14HashSetSavePersion {
public static void main(String[] args) {
HashSet<persions> set=new HashSet<>();
persions p1=new persions("小明",21);
persions p2=new persions("小明",21);
persions p3=new persions("小明",24);
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println(p1.hashCode());//hashCode和equals方法重写前:460141958
//重写后:23458775
System.out.println(p2.hashCode());//hashCode和equals方法重写前:1163157884
//重写后:23458775
System.out.println(p1==p2);//hashCode和equals方法重写前:false
//重写后:false
System.out.println(p1.equals(p2));//hashCode和equals方法重写前:false
//重写后:true
System.out.println(set);//hashCode和equals方法重写前:[persions{name='小明', age=24}, persions{name='小明', age=21}, persions{name='小明', age=21}]
//重写后:[persions{name='小明', age=21}, persions{name='小明', age=24}]
}
}
class persions{
private String name;
private int age;
public persions(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
persions persions = (persions) o;
return age == persions.age &&
Objects.equals(name, persions.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "persions{" +
"name='" + name + '\'' +
", age=" + 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;
}
}
8.5LinkedHashSet
我们知道HashSet保证元素唯一,但是元素存进去是没有顺序的,要保证有序如何实现。
在HashSet下面有一个子类java.util.LinkedHashSet
,它是链表和哈希表组合的一个数据存储结构。
代码演示如下:
package Demo02;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
public class Demo15LinkedHashSet {
public static void main(String[] args) {
Set<String> set=new LinkedHashSet<>();
set.add("ddd");
set.add("bbb");
set.add("ccc");
set.add("aaa");
set.add("sss");
Iterator<String> iterator=set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
具有可预知迭代顺序的 Set
接口的哈希表和链接列表实现。此实现与 HashSet
的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。注意,插入顺序不 受在 set 中重新插入的 元素的影响。(如果在 s.contains(e)
返回 true
后立即调用 s.add(e)
,则元素 e
会被重新插入到 set s
中。)
此实现可以让客户免遭未指定的、由 HashSet
提供的通常杂乱无章的排序工作,而又不致引起与 TreeSet
关联的成本增加。使用它可以生成一个与原来顺序相同的 set 副本,并且与原 set 的实现无关:
void foo(Set s) {
Set copy = new LinkedHashSet(s);
...
}
如果模块通过输入得到一个 set,复制这个 set,然后返回由此副本决定了顺序的结果,这种情况下这项技术特别有用。(客户通常期望内容返回的顺序与它们出现的顺序相同。)
此类提供所有可选的 Set
操作,并且允许 null 元素。与 HashSet
一样,它可以为基本操作(add
、contains
和 remove
)提供稳定的性能,假定哈希函数将元素正确地分布到存储段中。由于增加了维护链接列表的开支,其性能很可能会比 HashSet
稍逊一筹,不过,这一点例外:LinkedHashSet
迭代所需时间与 set 的大小 成正比,而与容量无关。HashSet
迭代很可能支出较大,因为它所需迭代时间与其容量 成正比。
链接的哈希 set 有两个影响其性能的参数:初始容量 和加载因子。它们与 HashSet
中的定义极其相同。注意,为初始容量选择非常高的值对此类的影响比对 HashSet
要小,因为此类的迭代时间不受容量的影响。
注意,此实现不是同步的。如果多个线程同时访问链接的哈希 set,而其中至少一个线程修改了该 set,则它必须 保持外部同步。这一般通过对自然封装该 set 的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedSet
方法来“包装”该 set。最好在创建时完成这一操作,以防止意外的非同步访问:
Set s = Collections.synchronizedSet(new LinkedHashSet(...));
此类的 iterator
方法返回的迭代器是快速失败 的:在迭代器创建之后,如果对 set 进行修改,除非通过迭代器自身的 remove
方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException
。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。
注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何强有力的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException
。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。
此类是 Java Collections Framework 的成员。
8.6 可变参数
tips:当使用参数类型确定,而个数不确定时,可以使用可变参数
在JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格式:
修饰符 返回值类型 方法名(参数类型...形参名){ }
等价于
修饰符 返回值类型 方法名(参数类型[] 形参名){ }
只是后面这种定义,在调用时必须传递数组,而前者可以直接传递数据即可。
JDK1.5以后。出现了简化操作。…用在参数上,称之为可变参数。
同样是代表数组,但是在调用这个带有可变参数的方法时,不用创建数组(这就是简单之处),直接将数组中的元素作为实际参数进行传递,其实编译成的class文件,将这些元素先封装到一个数组中,在进行传递。这些动作都在编译.class文件时,自动完成了。
原理:可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数,传递的参数个数,可以是0个(不传递),1,2. . .多个
package Demo02;
public class Demo16VarArgs {
public static void main(String[] args) {
// int i = add();
// System.out.println(i);[I @1b6d3586 [代表数组 I代表int类型
int s=add(10,20,30,40);
System.out.println(s);//100
}
public static int add(int...arr){
System.out.println(arr.length);//4
int sum=0;
for (int i:arr) {
sum+=i;
}
return sum;
}
}
注意事项:1.一个方法的参数列表,只能有一个可变参数
// public static void method(int...a,String...b){ 会报错
//
// }
public static void method(String...b){
}
2.如果方法的参数有多个,那么可变参数必须写在参数列表的末尾
// public static void method(int...i,int a,String b,double c){ 会报错
//
// }
public static void method(String b,int a,double c,int...i){
}
第九章 Collections
9.1常用功能
java.util.Collections
是集合工具类,用来对集合进行操作,部分方法如下:public static <T> boolean addAll(Collection<T> c,T... elements)
:向集合中添加一些元素。public static void shuffle(List<?> list)
:打乱集合元素的顺序。public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序public static <T> void sort(List<T> list,Comparator<? super T>)
:将集合中元素按照指定规则排序
tips:sort(List
list)使用前提:被排序的集合里的元素,必须实现Comparable,重写接口中的方法comparTo定义排序的规则
package Demo02;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Demo17Collections {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
/*
向集合中添加多个元素
*/
Collections.addAll(list,"a","b","c","d","e");
System.out.println(list);//[a, b, c, d, e]
/*
打乱集合中元素的顺序
*/
Collections.shuffle(list);
System.out.println(list);
/*
集合中元素按照默认方式排序
*/
Collections.sort(list);//默认是升序
System.out.println(list);
/*
集合中元素按照指定方式排序
*/
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return 0;
}
});
System.out.println(list);
}
}
自定义类型
Comparable接口的排序顺序:
自己(this)-参数:升序
参数-自己(this):降序
Comparator和Comparable的区别
Comparable:自己(this )和别人(参数)比较,自己需要实现comparable接口,重写比较的规则compareTo方法
Comparator:相当于找—个第三方的裁判,比较两个
package Demo02;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Demo18Sort {
public static void main(String[] args) {
ArrayList<Persion> list=new ArrayList<>();
list.add(new Persion("小明",21));
list.add(new Persion("a小李",19));
list.add(new Persion("小张",29));
list.add(new Persion("b小王",19));
Collections.sort(list);
System.out.println(list);//[Persion{name='小明', age=21}, Persion{name='小李', age=22}, Persion{name='小张', age=29}, Persion{name='小王', age=19}]
System.out.println("---------------------------");
Collections.sort(list, new Comparator<Persion>() {
@Override
public int compare(Persion o1, Persion o2) {
// return o1.getAge()-o2.getAge();按年龄升序排序
// return o2.getAge()-o1.getAge();按年龄降序排序
int result=o1.getAge()-o2.getAge();//按年龄升序排序
if(result==0){
result= o1.getName().charAt(0)-o2.getName().charAt(0);//首字母排序
}
return result;
}
});
System.out.println(list);
}
}
class Persion implements Comparable<Persion>{
private String name;
private int age;
public Persion(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Persion{" +
"name='" + name + '\'' +
", age=" + 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 int compareTo(Persion o) {
return 0;//认为元素都是相同的
/*
可以自定义比较方法 比较两人的年龄(this,参数Person)
return this.getAge()-o.getAge()//按年龄升序排序
return o.getAge()-this.getAge()//按年龄降序排序
*/
}
}
第十章 Map集合(双列集合)
10.1概述
前几章我们学习的都是单列集合,而Map是双列集合。
Map<K,V>,K我们称为键(此映射所维护的键的类型),V我们称为值(映射值的类型),键是不允许重复的,value可以重复。
tips:Map集合中K和V的关系类似于函数自变量和函数值的关系。
在现实生活中,我们常会看到这样一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类来存放这种对象关系的对象,即java.util.Map
接口。
对比Collection接口与Map接口的不同:
10.2Map常用子类
HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
java.util.HashMap<K,V>集合 implements Map<K,V>接口
特点:1. HashMap集合底层是哈希表:查询速度特别快
JDK1.8之前:数组+单向链表
JDK1.8之后:数组+单向链表/红黑树(当链表长度超过8):提高查询的速度
2.HashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致
LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
java.util.LinkedHashMap<K,V>集合 extends HashMap<K,V>集合
特点:1. LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
2.LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
tips:Map接口中的集合都有两个泛型变量<K,V>,在使用时,要为两个泛型变量赋予数据类型。两个泛型变量<K,V>的数据类型可以相同,也可以不同。
10.3Map接口中的常用方法
public V put(k Key,V value)
:把指定的键与指定的值添加到Map集合中返回值:V
存储时,Key不重复,返回值V时null
存储时,Key重复,会使用新的value替换Map中重复的value,返回被替换的value值
public V remove(Object key)
:把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。返回值:V
Key存在,V返回被删除的值
Key不存在,V返回null
public V get(Object Key)
:根据指定的键,在Map集合中获取对应的值。返回值:V
Key存在,V返回该K对应的值
Key不存在,V返回null
boolean containsKey(Object Key)
:判断集合中是否包含指定的键。 Key存在,返回true
Key不存在,返回false
public Set<K> KeySet()
:获取Map集合中所有的键,存储到Set集合中。public Set<Map.Entry<K,V>> entrySet()
:获取到Map集合中所有的键值对对象的集合(Set集合)。
package Demo03;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Demo01Map {
public static void main(String[] args) {
// show01();
// show02();
show03();
}
/*
public Set<Map.Entry<K,V>> entrySet():获取到Map集合中所有的键值对对象的集合(Set集合)。
*/
public static void show03(){
HashMap<String,Integer> map03= new HashMap<>();
map03.put("小明",176);
map03.put("小华",178);
map03.put("小金",169);
map03.put("小林",183);
/*
boolean containsKey(Object Key):判断集合中是否包含指定的键。
*/
boolean b1 = map03.containsKey("小金");
System.out.println(b1);//true
boolean b2 = map03.containsKey("小李");
System.out.println(b2);//false
System.out.println(map03);//{小林=183, 小明=176, 小华=178, 小金=169}
System.out.println("---------------------");
Set<Map.Entry<String, Integer>> set = map03.entrySet();
System.out.println(set);//[小林=183, 小明=176, 小华=178, 小金=169]
}
/*
public V remove(Object key):把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
*/
public static void show02(){
HashMap<String,Integer> map02= new HashMap<>();
map02.put("小明",87);
map02.put("小华",97);
map02.put("小金",87);
map02.put("小林",99);
System.out.println(map02);
Integer a1 = map02.remove("小李");
System.out.println(a1);//null
System.out.println("----------------");
Integer a2 = map02.remove("小金");
System.out.println(a2);//87
/*
public V get(Object Key):根据指定的键,在Map集合中获取对应的值。
*/
Integer a3 = map02.get("小林");
System.out.println(a3);//99
Integer a4 = map02.get("小琳");
System.out.println(a4);//null
}
/*
public V put(k Key,V value):把指定的键与指定的值添加到Map集合中
*/
public static void show01(){
HashMap<String,String> map01= new HashMap<>();
String s1 = map01.put("计算机", "鼠标");
System.out.println(s1);//null
String s2 = map01.put("计算机", "键盘");
System.out.println(s2);//鼠标 返回的是被替换的值
map01.put("硬件","软件");
map01.put("java","C++");
map01.put("C","C++");
System.out.println(map01);//{java=C++, C=C++, 硬件=软件, 计算机=键盘}
/*
public Set<K> KeySet():获取Map集合中所有的键,存储到Set集合中。
*/
System.out.println("----------------------------");
Set<String> set = map01.keySet();
System.out.println(set);//[java, C, 硬件, 计算机]
}
}
10.4 Map集合遍历键找值方式
键找值方式:即通过元素中的键,获取键所在的值
分析步骤:
1.获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。方法提示KeySet()
2.遍历键的Set集合,得到每一个键
3.根据键,获取键所对应的值。方法提示get(K key)
package Demo03;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
public class Demo02KeySet {
public static void main(String[] args) {
show01();
}
public static void show01() {
HashMap<String, Integer> map = new HashMap<>();
map.put("小明", 176);
map.put("小华", 178);
map.put("小金", 169);
map.put("小林", 183);
/*
public Set<K> KeySet():获取Map集合中所有的键,存储到Set集合中。
*/
Set<String> set = map.keySet();
System.out.println(set);//[小林, 小明, 小华, 小金]
System.out.println("----------------------------");
/*
迭代器遍历集合
*/
Iterator<String> iterator=set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
/*
增强for
*/
for (String Key:set) {
Integer integer01 = map.get(Key);//通过Key找Value
System.out.println(integer01);
}
for (String Key:map.keySet()) {
Integer integer02 = map.get(Key);//通过Key找Value
System.out.println(integer02);
}
}
}
}
10.5Entry键值对对象
Map中存放的是两种对象,一种称为Key(键),一种称为value(值),它们在Map中是一一对应关系,这一对对象又称做Map中的一个Entry(项)。Entry将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键和对应的值。
获取对应的键和对应的值的方法:
public K getKey( )
:获取Entry对象中的键。public V getValue( )
:获取Entry对象中的值。
获取所有Entry对象的方法:
public Set<Map.Entry<K,V>> entrySet()
:获取到Map集合中所有的键值对对象的集合(Set集合)
10.6Map集合遍历键值对方式
步骤:
1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
2.遍历Set集合,获取每一个Entry对象
3.使用Entry对象中的方法getKey()和getValue()获取键与值
package Demo03;
import java.security.Key;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Demo03EntrySet {
public static void main(String[] args) {
show01();
}
public static void show01(){
HashMap<String,Integer> map=new HashMap<>();
map.put("小明",87);
map.put("小华",97);
map.put("小金",87);
map.put("小林",99);
Set<Map.Entry<String, Integer>> set = map.entrySet();
System.out.println(set);//[小林=99, 小明=87, 小华=97, 小金=87]
System.out.println("------------------------------------");
Iterator<Map.Entry<String,Integer>> iterator=set.iterator();
while (iterator.hasNext()){
Map.Entry<String,Integer> entry01=iterator.next();
String key01 = entry01.getKey();
Integer value01 = entry01.getValue();
System.out.println(key01+"="+value01);
// 小林=99
// 小明=87
// 小华=97
// 小金=87
/*
通过增强for
*/
System.out.println("--------------------------------");
for (Map.Entry<String,Integer> entry02:set) {
String key02 = entry02.getKey();
Integer value02 = entry02.getValue();
System.out.println(key02+"="+value02);
}
}
}
}
10.7HashMap存储自定义类型键值
练习:每位学生(姓名,年龄)都有自己的家庭地址。那么,既然有对应关系,则将学生对象和家庭住址存储到Map集合中。学生作为键,家庭住址作为值。
tips:学生姓名相同并且年龄相同视为同一名学生
package Demo03;
import jdk.internal.org.objectweb.asm.tree.analysis.Value;
import java.security.Key;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public class Demo04HashMapSavePerson {
public static void main(String[] args) {
// show01();
show02();
}
public static void show02(){
HashMap<Student,Integer> map=new HashMap<>();
map.put(new Student("小明",18),178);
map.put(new Student("小李",19),175);
map.put(new Student("小林",20),179);
map.put(new Student("小金",18),183);
map.put(new Student("小笠",18),178);
map.put(new Student("小笠",18),178);
Set<Map.Entry<Student, Integer>> set = map.entrySet();
for (Map.Entry<Student,Integer> a:set) {
Integer value = a.getValue();
Student key = a.getKey();
System.out.println(key+"---->"+value);
}
}
/*
HashMap存储自定义类型键值
key : string类型
string类重写hashcode方法和equals方法,可以保证key唯一
value : Person类型
value可以重复(同名同年龄的人视为同一个)
*/
public static void show01(){
HashMap<String,Student> map=new HashMap<>();
map.put("北京",new Student("小明",18));
map.put("上海",new Student("小李",19));
map.put("北票",new Student("小林",20));
map.put("沈阳",new Student("小金",18));
map.put("哈尔滨",new Student("小笠",18));
map.put("哈尔滨",new Student("小笠",18));
Set<String> set = map.keySet();
for (String Key: set) {
Student value=map.get(Key);
System.out.println(Key+"--->"+value);
}
}
}
class Student{
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public Student(){
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + 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;
}
}
10.7LinkedHashMap
HashMap保证元素唯一,并且查询速度很快,可是我们要保证元素有序,还要速度快,就要了解LinkedHashMap,它是HashMap下面有一个子类,它是链表和哈希表组合的一个数据存储结构。
package Demo03;
import java.security.Key;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
public class Demo05LinkedHashMap {
public static void main(String[] args) {
show01();
}
public static void show01(){
LinkedHashMap<String,Integer> map=new LinkedHashMap<>();
map.put("小明",18);
map.put("小李",20);
map.put("小林",18);
Set<Map.Entry<String, Integer>> set = map.entrySet();
for (Map.Entry<String, Integer> entry:set) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key+"--->"+value);
// 小明--->18
// 小李--->20
// 小林--->18
}
}
}
10.8Hashtable集合
Hashtable<k,v> implements Map<K,V> 双列集合(JDK1.0)比较早
此类实现了一个哈希表,该哈希表将键映射到相应的值。任何非null对象都可以用做键或值。Hashtable是同步的(单线程):速度慢
HashtabLe:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢,不能存储null值, null键
HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快,可以存储null值, null键
Hashtable和vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap , ArrdyList)取代了
HashtabLe的子类Properties依然活跃在历史舞台 Properties集合是一个唯—和I0流相结合的集合
package Demo03;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
public class Demo05Hashtable {
public static void main(String[] args) {
// show02();
show01();
}
public static void show02(){
HashMap<String,String> map=new HashMap<>();
map.put(null,"a");
map.put("a",null);
map.put(null,null);
System.out.println(map);//{null=null, a=null}
}
public static void show01(){
Hashtable<String,String> hashtable=new Hashtable<>();
hashtable.put(null,"a");//NullPointerException
hashtable.put("a",null);//NullPointerException
hashtable.put(null,null);//NullPointerException
System.out.println(hashtable);
}
}
10.9Map集合练习
需求:
计算一个字符串中每个字符出现次数
分析
1.获取一个字符串对象
2.创建一个Map集合,键代表字符,值代表次序
3.遍历字符串得到每个字符
4.判断Map中是否有该键
5.如果没有,第一次出现,存储次数为1;如果有,则说明已经出现过,获取到对应的值进行++,再次存储
6.打印最终结果
分析:
1.使用scanner获取用户输入的字符串
2.创建Map集合,key是字符串中的字符, value是字符的个数
3.遍历字符串,获取每一个字符
4.使用获取到的字符,去Nap集合判断key是否存在
key存在:
通过字符(key) ,获取value(字符个数)value++
put( key , value)把新的vatue存储到Map集合中
key不存在:
put( key ,1)
5.遍历Map集合,输出结果
package Demo03;
import java.security.Key;
import java.util.HashMap;
import java.util.Scanner;
public class Demo06MapTest {
public static void main(String[] args) {
show01();
}
public static void show01(){
Scanner in=new Scanner(System.in);
System.out.println("请输入字符串:");
String s=in.next();
HashMap<Character,Integer> map=new HashMap<>();
for (char c:s.toCharArray()) {
if (map.containsKey(c)){
Integer value = map.get(c);
value++;
map.put(c,value);
}else {
map.put(c,1);
}
}
for (Character Key:map.keySet()) {
Integer value = map.get(Key);
System.out.println(Key+"--->"+value);
}
}
}
10.10JDK9对集合添加的优化
添加元素时,List和Set集合使用add方法,Map集合使用put方法
在JDK9时,新的List、Set、Map的静态工厂方法可以更方便地创建集合的不可变实例。
of方法(了解即可)
附加:Debug追踪
Debug调试程序:
可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug。
使用方式:
在行号的右边,鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里)
右键,选择Debug执行程序
程序就会停留在添加的第一个断点处
执行程序:
f8:逐行执行程序
f7:进入到方法中
shift+f8:跳出方法
f9:跳到下一个断点,如果没有下一个断点,那么就借书程序
ctrl+f2:退出debug模式,停止程序
Console:切换到控制台
- 本文作者: ShanTianQi
- 本文链接: https://stq555.com/2022/01/08/%E9%9B%86%E5%90%88/index.html
- 版权声明:本博客所有文章均采用 BY-NC-SA 许可协议,转载请注明出处!