集合
文章目录
绪论
集合和数组的区别:
-
长度:数组长度是不可变的,集合的长度是可变的。
-
存储的数据类型:
-
数组存储的是统一类型的元素,可以存储基本数据数据类型值。
-
集合存储的都是对象,而且对象的类型可以不一致,开发中对象较多时,应使用集合进行存储。
-
集合按照存储可以分为两大类:单列集合和双列集合
-
单列集合
java.util.Collection
-
双列集合
java.util.Map
学习集合的目标:
- 会使用集合存储数据
- 会遍历集合,取出数据
- 掌握每种集合的特性
集合框架的学习方式:
- 学习顶层:学习顶层接口、抽象类中共性的方法,所有的子类都可以使用
- 使用底层:顶层都是接口、抽象类,无法直接创建对象使用,需要使用底层的子类创建对象使用
一、集合相关知识
1、迭代器接口 java.util.Iterator
-
作用:对集合进行遍历
-
常用方法:
boolean hasNext() 判断集合中还有没有下一个元素,有返回true,没有则返回false
E next() 取出集合中的下一个元素
Iterator迭代器是一个接口,调用方法的话需要使用实现类对象 ,Collection接口当中有一个方法,叫iterator(),这个方法的返回值就是Iterator实现类对象。
- 代码实例:
// 创建一个集合对象
Collection<String> coll = new ArrayList<>();
// 在集合中添加元素
coll.add("姚明");
coll.add("詹姆斯");
coll.add("德布劳内");
coll.add("德布劳内");
coll.add("德布劳内");
// 获取Iterator实现类对象
Iterator<String> iterator = coll.iterator();
// 使用while循环遍历
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("=============");
Iterator<String> it = coll.iterator();
// 使用for循环进行遍历
for (int i = 0; i < coll.size(); i++) {
System.out.println(it.next());
}
2、增强for循环
- 增强for循环:底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写。是JDK1.5之后出现的特性。
public interface Collection<E> extends Iterable<E> :所有的单列集合都可以使用增强for循环(接口继承接口)
public interface Iterable<T>:实现这个接口,允许对象成为"foreach"语句的目标。
Implementing this interface allows an object to be the target of the "for-each loop" statement.
- 作用:遍历数组和集合
3、泛型
泛型:是一种未知的数据类型,当我们不知道使用什么数据类型的时候,可以使用泛型。泛型也可以看做是一种变量,用来接收数据类型。
可能就算看了以下泛型的知识点之后可能还是会有所困惑,这里先回答三个问题泛型到底是干嘛的?为什么要用泛型?我们正常写程序用的到吗?
为了回答以上问题,参考《JAVA-核心技术卷1》关于泛型的章节,首先回答以下三个问题。
泛型到底是干嘛的?
为什么要用泛型?
为了让编写的代码可以被很多不同类型的对象所重用。使用泛型的好处:使用一些方法时避免进行类型强制转换,将可能出现在运行时的异常暴露在代码编译阶段,同时增加程序的可读性及安全性。
我们正常写程序用得到吗?
书中提到三个使用级别,一是仅仅使用泛型类,如ArrayList;二是系统学习泛型来解决泛型类混在一起时可能产生的含混不清的错误消息;三是自行编写泛型类和泛型方法。大部分人停留在第一级别(基本够用)
-
创建集合对象的时候,就会确认泛型类型。
-
使用泛型创建集合:
ArrayList<String> list = new ArrayList<>(); list.add("abd"); list.add("cccd"); Iterator<String> it = list.iterator(); while(it.hasNext()) { String s = it.next(); System.out.println(s+"->" +s.length()); }
优点:①避免了类型装换的麻烦,存储的是什么类型,取出就是什么类型
②把运行期异常(代码运行之后会抛出异常)提升到了编译期
缺点:泛型是什么类型,只能存储什么类型的数据 -
不使用泛型创建集合:
ArrayList list = new ArrayList(); list.add("abd"); list.add(1); // 使用迭代器遍历集合 Iterator it = list.iterator(); while(it.hasNext()) { // 取出元素是obj类型 Object obj = it.next(); System.out.println(obj); // 想要使用String特有的方法,需要向下转型 // 会抛出异常 类型转换异常,不能将Integer对象转换成String对象 String s = (String) obj; System.out.println(s);// java.lang.ClassCastException }
优点:默认类型是Object类型,可以存储任意类型的数据
缺点:不安全,会拋异常
-
-
泛型类
定义一个含有泛型的类,模拟ArrayList集合。定义对象的时候确认泛型类型。
public class GenericClass<E> { // 泛型类 private E name; public E getName() { return name; } public void setName(E name) { this.name = name; } }
-
泛型方法
定义含有泛型的方法:泛型定义在方法的修饰符和返回值之间。含有泛型的方法,在调用泛型的时候确定泛型的数据类型传递什么类型的参数,泛型就是什么类型。
// 泛型方法的定义 public class GenericMethod { public <M> void method1(M m){ System.out.println(m); } public static <T> void method2(T t) { System.out.println(t); } } // 创建对象并调用泛型方法 public class Demo03GenericMethod { public static void main(String[] args) { // 创建GenericMethod对象 GenericMethod gm = new GenericMethod(); // 调用泛型方法 gm.method1(1); gm.method1(0.8); gm.method1("hello"); gm.method1(false); // 调用静态泛型方法 gm.method2("hi"); GenericMethod.method2(0.888); } }
-
泛型接口
两种接口使用泛型的方式: 1、实现类中确认泛型;2、创建实现类对象的时候确认泛型。
public interface GenericInterface<I>{ public abstract void method(I i); } // 1、实现类中确认泛型 /* 含有泛型的接口,第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型 例: public interface Iterator<E> { E next(); } Scanner类实现了Iterator接口,并指定接口的泛型为String,所以重写的next方法泛型默认就是String public final class Scanner implements Iterator<String>{ public String next(){} } */ public class GenericInterfaceImpl1 implements GenericInterface<String>{ @Override public void method(String s) { System.out.println(s); } } // 2、接口泛型的第二种实现方式:实现类仍然写泛型,在创建实现类对象的时候再指定泛型类型 public class GenericInterfaceImpl2<I> implements GenericInterface <I>{ @Override public void method(I i) { System.out.println(i); } } // 创建实现类对象并调用方法 public class Demo04GenericInterface { public static void main(String[] args) { // 接口泛型的第一种使用方法:定义实现类的时候指定泛型类别 GenericInterfaceImpl1 gi = new GenericInterfaceImpl1(); gi.method("hello"); // 接口泛型的第二种使用方法:创建实现类对象的时候指定泛型类型 GenericInterfaceImpl2<Double> gi2 = new GenericInterfaceImpl2(); gi2.method(0.88); } }
-
泛型通配符
?:代表任意的数据类型
使用方式:不能创建对象使用,只能作为方法的参数使用。
public class Demo05Generic { public static void main(String[] args) { ArrayList<Integer> list1 = new ArrayList<>(); list1.add(1); list1.add(2); ArrayList<String> list2 = new ArrayList<>(); list2.add("a"); list2.add("b"); } // 定义一个方法,能够遍历所有类型的ArrayList集合 // 这时候我们不知道ArrayList集合使用的什么数据类型,需要使用泛型的通配符?来接收数据 // 注意:泛型是没有继承概念的 public void printArray(ArrayList<?> list){ Iterator<?> it = list.iterator(); while(it.hasNext()){ Object o = it.next(); System.out.println(o); } } }
泛型通配符的高阶用法:(了解)
泛型的上限限定: ? extends E 代表使用的泛型只能是E的子类/本身
泛型的下限限定: ? super E 代表使用的泛型只能是E的父类/本身
一、Collection
1、List接口
java.util.List接口 extends Collection接口
(1)List接口特点:
- 有序的集合,存储元素和取出元素的顺序是一致的
- 有索引,包含了一些带索引的方法
- 允许存储重复的元素
(2)List接口中带索引的方法:
public void add(int index,E element):将指定的元素添加到该集合的指定位置
public E get(int index) :返回集合中指定位置的元素
public E remove(int index):移除列表中指定位置的元素,返回该元素
public E set(int index,E element):用指定元素替换集合中指定位置的元素,返回更新前的元素
(3)List接口的三种遍历方法
// list的遍历有三种方法
// 第一种使用for循环索引遍历
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// 第二种:使用迭代器
Iterator<String> it = list.iterator();
while(it.hasNext()){
String next = it.next();
System.out.println(next);
}
// 第三种:增强for循环
for (String s : list) {
System.out.println(s);
}
(4)List接口的实现类
-
ArrayList
-
java.util.ArrayList
集合数据存储结构是数组结构,是List接口的大小可变数组的实现。注意,此实现不是同步的,意味着采用了多线程,速度更快。 -
元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合。
-
查找快是因为数组的地址是连续的。查找快是因为每次增加或者删除一个元素,底层都要调用复制的方法,将元素复制一遍,重新生成一个新的数组。(add方法的源码)
-
许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严禁,这种用法是不提倡的。
-
创建可以使用多态
-
-
LinkedList
-
java.util.LinkedList
集合也实现了List接口,存储数据的结构是链表结构。方便元素添加、删除。 -
LinkedList是一个双向链表,实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。
-
特点:
-
底层是一个链表结构,查询慢、增删快
-
里面包含了大量操作首尾元素的方法
-
使用LinedList集合特有的方法,不能使用多态
LinkedList<String> linked = new LinkedList<>();
-
-
特有方法:
public void addFirst(E e) public void addLast(E e) public E getFirst() public E getLast() public E removeFirst() public E removeLast() public E pop() public void push(E e) public boolean isEmpty()
-
注意事项:
linkedList.clear()
清空集合中的元素,在获取集合中的元素会抛出NoSuchElementException
异常,因此可以在获取元素之前加个判断:if(!linkedList.isEmpty())
创建LinkedList集合的时候不能使用多态,否则无法访问到该类的特有方法
LinkedList<String> linkedList = new LinkedList<>();
-
-
Vector(了解)
java.util.Vector
可以实现可增长的对象数组,类似于动态数组的功能。与数组一样,他可以使用整数索引进行访问的组件。但是,Vector的大小可以根据需要增大或缩小,以适应创建Vector后进行添加或移除项的操作。- 与新collection实现不同,Vector是同步的。
2、Set接口
java.util.Set接口 extends Collection接口
(1)Set接口的特点:
- 不允许存储重复的元素
- 没有索引,不能使用带索引的方法,不能使用普通for循环进行遍历
(2)Set接口的实现类:
①java.util.HashSet
实现了Set接口,由哈希表支持(实际上是HashMap的实例,底层是通过HashMap实现的)
jdk1.8之前,哈希表= 数组+链表。
jdk1.8之后,哈希表= 数组+链表 ; 哈希表=数组+红黑树(提高查询的速度)。如果链表的长度超过8位,就会将链表转换为红黑树,目的是提高查询速度。
-
哈希值HashCode
哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来的地址,不是数据实际存储的物理地址)
在Object类中有个方法,可以获取对象的哈希值
int hashCode()
返回该对象的哈希码值。//hashCode方法的源码 public native int hashCode(); // native:代表该方法调用的是本地操作系统的方法 // 哈希冲突:不同的内容hashCode相同 System.out.println("重地".hashCode());// 1179395 System.out.println("通话".hashCode());// 1179395
-
除了包含Set接口的特点,还具备以下特点:
-
元素是无序的(存集合所说的序,是指元素存入集合的顺序,当元素存储顺序和取出顺序一致时就是有序,否则就是无序。)
-
底层是哈希表结构,特点是查询的速度特别快。
此实现也不是同步的,使用了多线程。
// 多态创建一个HashSet集合 Set<String> hashSet = new HashSet<>(); hashSet.add("helo"); hashSet.add("a"); hashSet.add("c"); hashSet.add("5"); hashSet.add("5"); hashSet.add("喝了两"); hashSet.add("文件访问了"); hashSet.add("adfasd"); hashSet.add("c"); System.out.println(hashSet); // 使用迭代器遍历 Iterator<String> it = hashSet.iterator(); while(it.hasNext()){ String next = it.next(); System.out.println(next); } // 使用增强for遍历 for(String str : hashSet) { System.out.println(str); }
-
-
不允许存储重复元素的原理:
Set集合存储不重复的元素,前提是存储的元素类必须重写hashCode方法和equals方法。Set集合在调用add方法的时候,add方法会调用元素的hashCode方法和equals方法,判断元素是否重复。String类型重写了Object类的hashCode方法,“重地”和“通话”哈希值相同,称之为哈希冲突。
// 创建HashSet集合对象 HashSet<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);
-
HashSet存储自定义类型数据:
保证元素唯一:存储的元素(String,Integer,…Student, Person…),必须重写hashCode()方法和equals()方法。
// 自定义Person类,要求:同名同年龄的人,视为同一个人,只能存储一次 public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } // 重写equals方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } // 重写hashCode方法 @Override public int hashCode() { return Objects.hash(name, 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; } } public class Demo04HashSetSavePerson { public static void main(String[] args) { // 创建HashSet集合存储Person HashSet<Person> set = new HashSet<>(); Person p1 = new Person("小美女",18); Person p2 = new Person("小美女",18); Person p3 = new Person("小美女",19); // 重写完hashCode方法后,两个哈希值相等 System.out.println(p1.hashCode());// 356573597 System.out.println(p2.hashCode());// 1735600054 System.out.println(p1 == p2); // false System.out.println(p1.equals(p2));// false 重写完equals方法后返回true set.add(p1); set.add(p2); set.add(p3); // 重写完hashCode方法和equals方法后,p1和p2重复,只能添加一个 System.out.println(set);// [Person{name='小美女', age=18}, Person{name='小美女', age=18}, Person{name='小美女', age=19}] } }
②java.util.LinkedHashSet集合 extends HashSet集合
- 特点:底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序),保证元素有序。
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("www");
linkedHashSet.add("abc");
linkedHashSet.add("abc");
linkedHashSet.add("itcast");
System.out.println(linkedHashSet);//[www, abc, itcast] 有序,不允许重复
3、可变参数
可变参数,jdk1.5之后出现的新特性。
-
使用前提:当参数列表的数据类型已经确定,但是参数的个数不确定,可以使用可变参数。
-
使用格式:定义方法时使用
修饰符 返回值类型 方法名(数据类型...变量名){}
-
原理:
可变参数的层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数。
传递的参数个数,可以是0(不传递),1,2…多个。
-
注意事项:
- 一个方法的参数列表,只能有一个可变参数
- 如果方法的参数有多个,那么可变参数必须写在参数列表的末尾
-
特殊(终极)写法:
public static void method(Object...obj){} //可以接收任意类型的参数
-
使用实例:
/* 定义计算(0-n)整数和的方法 已知:计算整数的和,数据类型已经确定int 但是参数的个数不知道,就可是使用可变参数 add();会创建一个长度为0的数组 new int[0] add(10);会创建一个长度为1的数组 new int[]{10} add(10,20);会创建一个长度为2的数组 new int[]{10,20} */ public static int add(int...arr){ // System.out.println(arr);// [I@1540e19d 底层是个数组 // System.out.println(arr.length);// 0 // 定义一个初始化的变量,记录累加求和 int sum = 0; // 遍历数组 for (int i : arr){ sum += i; } return sum; }
4、Collections工具类
java.util.Collections
是集合工具类,用来对集合进行操作。常用方法:
public static <T> boolean addAll(Collection<T> c, T... elements) //往集合中添加一些元素。
public static void shuffle(List<?> list) //打乱集合中元素顺序
/*
注意:
sort<List<T> list) 使用前提
被排序的集合里存储的元素类必须实现Comparable,重写接口中的方法compareTo方法定义排序的规则
Comparable接口的排序规则:
自己(this) - 参数:升序
*/
public satic <T> void sort(List<T> list) //将集合中的元素按照默认规则排序
/*
java.util.Collections是集合工具类,用来对集合进行操作。部分方法:
public static <T> void sort(List<T> list, Comparator<? super T>) : 将集合中元素按照指定规则排序。
Comparator和Comparable的区别
Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法
Comparator:相当于找一个第三方的裁判,比较两个
Comparator的排序规则:
o1 - o2 : 升序
*/
public static <T> void sort(List<T> list, Comparator<? super T>) //将集合中元素按照指定规则排序。
二、Map
java.util.Map<k,v>
集合
1、Map集合接口特点:
- Map 集合是一个双列集合,一个元素包含两个值(一个key,一个value)
- Map集合中的元素,key和value的数据类型可以相同,也可以不同
- Map集合中的元素,key是不允许重复的,value是可以重复的
- Map集合中的元素,key和value是一一对应的
2、Map接口的常用方法:
boolean containsKey(Object key)//判断集合中是否包含指定的键。包含返回true,不包含返回false
public V get(Object key) // 根据指定的键,在Map集合中获取对应的值。
// 返回值:key存在,返回对应的value值;key不存在,返回null
public V remove(Object key)// 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
//返回值V:key存在,返回被删除的值,key不存在,返回null
public V put(K key, V value)//把指定的键与指定的值添加到Map集合汇总
//返回值V:存储键值对的时候,key不重复,返回值是null;key重复,会使用新的value替换map中重复的value,返回被替换的value值
3、Map集合接口实现类
(1)HashMap
java.util.HashMap<k,v>集合 implements Map<k,v>接口
-
特点:
- HashMap集合底层是哈希表:查询的速度特别的快
JDK1.8之前:数组+单向链表
JDK1.8之后:数组+单向链表/红黑树(链表的长度超过8):提高查询的速度 - hashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致
- HashMap集合底层是哈希表:查询的速度特别的快
-
存储自定义数据类型
HashMap存储自定义类型的键值。Map集合保证key是唯一的:作为key的元素,必须重写hashCode和equals方法,以保证key唯一。
/* HashMap存储自定义类型键值 key:Person类型 重写的hashCode方法和equals方法来保证key唯一 value:String类型 value可以重复 */ private static void demo02() { Map<Person, String> map = new HashMap<>(); map.put(new Person("张三",18), "北京"); map.put(new Person("张三",18), "南京"); map.put(new Person("赵晓",22), "上海"); // entrySet遍历 Set<Map.Entry<Person, String>> entrySet = map.entrySet(); for (Map.Entry<Person, String> entry : entrySet) { Person key = entry.getKey(); String value = entry.getValue(); System.out.println(key + "====>" + value); } } /* HashMap存储自定义类型键值 key:String类型 String类重写的hashCode方法和equals方法可以保证key唯一 value:Person类型 value可以重复(同名同年龄的人视为同一个人) */ private static void demo01() { // 多态创建map集合 Map<String, Person> map = new HashMap<>(); // 往map集合中添加元素 map.put("北京",new Person("张三",18)); map.put("上海",new Person("李四",22)); map.put("南京",new Person("战友",12)); map.put("北京",new Person("赵晓",18)); // 使用keySet + 增强for循环遍历 Set<String> keys = map.keySet(); for (String key: keys) { System.out.println(key + "===>" + map.get(key)); } }
(2)LinkedHashMap
java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合
,Map 接口的哈希表和链表实现,具有可预知的迭代顺序。底层原理:哈希表 + 链表 (记录元素的顺序)
- 特点:
- LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序)
- LinkedHashMap是一个有序的集合,存储元素和取出元素的顺序是一致的
(3)Hashtable
java.util.Hashtable<K,V>集合 implements Map<K,V>接口
Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢;HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程集合,速度快。HashMap(之前学过的所有集合)可以存储null值,Hashtable集合不能存储 null值。
Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)所取代,Hashtable的子类Properties依然活跃在历史舞台。Properties集合是唯一一个和IO流结合的集合。
Hashtable<String, String> hashtable = new Hashtable<>();
//hashtable.put(null,"abc");// java.lang.NullPointerException
//hashtable.put("abc", null);// java.lang.NullPointerException
hashtable.put(null,null);// java.lang.NullPointerException
System.out.println(hashtable);
4、Map集合的两种遍历方式
(1)通过键找值的方式
// 需要使用的方法
Set<k> keySet() //返回此映射中包含的键的Set视图
-
实现步骤:
-
使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
-
遍历set集合,获取Map集合中的每一个key
-
通过Map集合中的方法get(key),通过key找到value
-
-
实例:
// 创建Map集合对象 Map<String,Integer> map = new HashMap<>(); map.put("杨过", 18); map.put("尹志平", 28); map.put("yanz", 38); //1、使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中 Set<String> set = map.keySet(); //2、遍历set集合,获取Map集合中的每一个key //使用迭代器遍历Set集合 Iterator<String> it = set.iterator(); while(it.hasNext()){ String key = it.next(); // 3、通过Map集合中的方法get(key),通过key找到value Integer value = map.get(key); System.out.println(key + "=" + value); } // 也可使用增强for循环 for(String key: map.keySet()) { Integer value = map.get(key); System.out.println(key + "=" + value); }
(2)使用Entry对象遍历
// 需要使用的方法
Set<Map.Entry<k,v> entrySet() // 返回此映射中包含的映射关系的Set视图
-
实现步骤
- 使用Map集合汇总的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
- 遍历Set集中,获取每一个Entry对象
- 使用Entry对象中的getKey() 和getValue() 获取键与值
-
实例:
//创建Map集合对象 Map<String,Integer> map = new HashMap<>(); map.put("杨过", 18); map.put("尹志平", 28); map.put("yanz", 38); //1、使用Map集合汇总的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中 Set<Map.Entry<String, Integer>> set = map.entrySet(); // 使用迭代器遍历Set集合 Iterator<Map.Entry<String, Integer>> it = set.iterator(); while (it.hasNext()) { Map.Entry<String, Integer> entry = it.next(); // 3、使用Entry对象中的getKey() 和getValue() 获取键与值 String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key + "=" + value); } // 也可使用增强for循环进行遍历
5、练习:计算一个字符串中每个字符出现的次数
/*
分析:
1、创建map集合,key是字符,value是次数
2、遍历字符串得到每个字符
3、判断集合中是否有改字符的键,有的话value++, 没有的话添加进去,存储次数为1
4、打印最终结果
*/
public class Demo03MapTest {
public static void main(String[] args) {
System.out.println("请输入一个字符串:");
// nextLine方法返回输入的字符串,回车结束
String line = new Scanner(System.in).nextLine();
findChar(line);
}
private static void findChar(String line) {
// 多态创建map集合
Map<Character, Integer> map = new HashMap<>();
for(int i = 0; i < line.length(); i++) {
char key = line.charAt(i);
// 判断集合中是否有该字符的键
// 先获取集合中所有的键
Set<Character> keySet = map.keySet();
if(keySet.contains(key)){
// 有的话,value+1
map.put(key, map.get(key) + 1 );
} else {
// 没有,增加新的键值对
map.put(key, 1 );
}
}
System.out.println(map);
}
}
三、JDK9的新特性
List接口、Set接口、Map接口:里面增加了一个静态的方法of,可以给集合一次性添加多个元素
static <E> List <E> of (E...elements)
1、使用前提:
当集合中存储的元素个数已经确定了,不再改变时使用
2、注意:
- of方法只适用于List接口,Set接口,Map接口,不适用于接口的实现类
- of方法的返回值是一个不能改变的集合,集合不能再使用add、put方法添加元素,否则会抛出异常
- Set接口和Map接口在调用of方法的时候,不能有重复元素,否则会抛出异常
3、实例:
List<String> list = List.of("a","b","c","d");
System.out.println(list);
list.add("e");// java.lang.UnsupportedOperationException
四、本章练习(斗地主)
思路:需要单独一个list集合来存储牌的索引,通过牌的索引来进行发牌和排序。
/*
1、组装一副牌
2、洗牌
3、发牌
4、排序
5、打印
*/
public class Demo01DouDiZhu {
public static void main(String[] args) {
// 1、组装一副牌
// 创建一个Map集合存储牌的索引和组装好的牌
HashMap<Integer, String> poker = new HashMap<>();
// 创建一个list集合存储牌的索引
List<Integer> pokerIndex = new ArrayList<>();
List<String> colors = List.of("♠","♣","♦","♥");
List<String> numbers = List.of("2","A","K","Q","J","10","9","8","7","6","5","4","3");
// 添加大王小王到map集合中,索引集合中添加0和1
int index = 0;
poker.put(index, "大王");
pokerIndex.add(index);
poker.put( ++index, "小王");
pokerIndex.add(index);
// 遍历添加元素
for (String number : numbers) {
for (String color : colors) {
poker.put(++index, color+number);
pokerIndex.add(index);
}
}
System.out.println(poker);
System.out.println(pokerIndex);
// 2、洗牌
Collections.shuffle(pokerIndex);
// 3、发牌
// 创建四个集合进行发牌
List<Integer> play01 = new ArrayList<>();
List<Integer> play02 = new ArrayList<>();
List<Integer> play03 = new ArrayList<>();
List<Integer> diPai = new ArrayList<>();
for (int i = 0; i < pokerIndex.size(); i++) {
if(i>50) {
diPai.add(pokerIndex.get(i));
} else if(i%3 == 0) {
play01.add(pokerIndex.get(i));
} else if(i%3 == 1) {
play02.add(pokerIndex.get(i));
} else if(i%3 == 2) {
play03.add(pokerIndex.get(i));
}
}
// 4、排序
Collections.sort(play01);
Collections.sort(play02);
Collections.sort(play03);
Collections.sort(diPai);
// 看牌
showPoker("play01",poker,play01);
showPoker("play02",poker,play02);
showPoker("play03",poker,play03);
showPoker("diPai",poker,diPai);
}
/*
params:
String name : 玩家姓名
Map<Integer,String> poker :扑克牌
List<Integer> pokerIndex :扑克牌索引
*/
private static void showPoker(String name, Map<Integer,String> poker, List<Integer> pokerIndex){
System.out.print(name + ":");
// 遍历集合
for (int i = 0; i < pokerIndex.size(); i++) {
System.out.print(poker.get(pokerIndex.get(i))+ " ");
}
System.out.println();
}
}