11. 集合
11.1 泛型
11.1.1 泛型的定义
泛型:泛指的类型,可以将类型在类与类、方法、接口之间进行传递,类似于方法的传参,其类型由使用方决定。
定义了泛型的方法、类、接口称泛型方法、泛型类、 泛型接口,定义时被定义在类名、接口名的后面,小括号、大括号的前面,定义泛型方法时则放在返回值类型之前。当使用泛型类、泛型接口时,需指定泛型的类型,否则默认为Object类型,而在泛型方法中,不能显式设置泛型的类型,必须将泛型在参数中体现出来,⾄少需要有⼀个参数是泛型类型的。。泛型只能被设置为引用数据类型,不能是基本数据类型。
泛型的类型名字,遵循大驼峰命名法,但一般情况下泛型都只用一个大写字母表示。
11.1.2 泛型的使用
例:定义泛型类和泛型接口:
class Person<T,M,D>{
}
interface Test<E>{
void show(E e);
}
class Man extends Person<String>{
}
class Woman<E> extends Person<E>{
}
class Test1 implements Test{
@Override
public void show(Object e){}
}
public class Program3 {
public static void main(String[] args) {
Person<String> xiaoming = new Person();
}
}
下面的例子演示了如何使用泛型方法打印不同字符串的元素:
public class GenericMethodTest
{
// 泛型方法 printArray
public static < E > void printArray( E[] inputArray )
{
// 输出数组元素
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
public static void main( String args[] )
{
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "Array integerArray contains:" );
printArray( intArray ); // 传递一个整型数组
System.out.println( "\nArray doubleArray contains:" );
printArray( doubleArray ); // 传递一个双精度型数组
System.out.println( "\nArray characterArray contains:" );
printArray( charArray ); // 传递一个字符型型数组
}
}
下面是定义泛型方法的规则:
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的)。
- 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型只能在当前方法、类、接口中使用,不能被继承;继承自泛型类、泛型接口的子类必须指定父类泛型的具体类型。
- 泛型方法方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
- 在使用泛型类和泛型接口时需显式标出泛型的类型,而在方法中则必须在方法声明时就在参数中体现泛型类型,在使用泛型方法时根据其参数类型隐式标出泛型的类型。
11.1.3 泛型限定
在调用泛型对象给另一个泛型类型赋值时,需明确泛型的类型,此时在<>内有以下限定:
符号 | 含义 |
---|---|
? | 通配 |
super 类名A | 泛型可以是A类及其父类 |
extends | 泛型可以是A类及其子类 |
11.2 集合简介
是一个存储数据的容器,
集合框架被设计成要满足以下几个目标。
- 该框架必须是高性能的。基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的。
- 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。
- 对一个集合的扩展和适应必须是简单的。
集合与数组的区别:
- 数组长度不可变,集合长度可变,并可以及时调整
- 数组中可以存储任意数据类型的元素,而集合中只能存储引用数据类型的元素(集合中可以存储包装类)。
- 数组方法单一,集合较灵活。
集合框架图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-raQN6S5C-1587711142146)(4.png)]
对集合的操作,按功能分为两大类:
1.增删改查
2.其他功能性操作
11.3 Collection接口
是集合框架中,单列集合的顶级接口,这个接口中定义了对单列集合进行的常规操作。
11.3.1 常用方法(重点在增删):
public static void main(String[] args) {
//1.通过一个实现类向上转型来实例化一个Collection接口的对象
Collection<Integer>collection = new ArrayList<>();
Collection<Integer>collection2 = setData();
//2.增:在集合末尾添加一个元素
collection.add(10);
//3.增:将目标参数集合的元素拼接到该集合的末尾
collection.addAll(collection);
//4.删:删除集合中首次出现的目标元素
//返回值代表删除操作的结果
boolean boolean4 = collection.remove(20);
//5.删:删除该集合中所有的在目标集合中出现的所有元素
boolean boolean5 = collection.removeAll(collection2);
//6.删:删除满足条件的所有元素,例:删除集合中大于20的元素(重要)
boolean boolean6 = collection.removeIf(ele->ele>20);
//7.保留两个集合的交集
collection.retainAll(collection2);
//8.清空集合
collection.clear();
//9.判断集合中是否包含指定的元素
boolean ret = collection.contains("Uncle wang");
//10.判断参数集合中的每⼀个元素是否都在当前集合中包含
boolean ret1 = collection.containsAll(list);
//11.判断集合每索引位元素是否完全相同
collection.equals(collection2);
//12.返回该集合的元素数量
int num10 = collection.size();
//13.判断该集合是否为空
boolean boolean11 = collection.isEmpty();
//14.转成 Object 数组
Object[] array = collection.toArray();
//15.转成指定类型的数组
String[] arr = collection.toArray(new String[0]);
//16.判断集合是否完全相同(先大小,而后依次进行等值比较)
collection.equals(collection2);
}
private static Collection<Integer> setData() {
//实例化Collection集合
Collection<Integer>List = new ArrayList<>();
List.add(10);
List.add(200);
List.add(300);
return List;
}
11.3.2 Collection集合的迭代
在进⾏集合的遍历的时候,⽅式其实很多。但是,基本上所有的遍历,都与 Iterable 接⼝有关。这个接
⼝提供了对集合集合进⾏迭代的⽅法。只有实现了这个接⼝,才可以使⽤增强for循环进⾏遍历。
-
增强for循环
注意事项:增强for循环中不允许对集合中的元素(长度)进行修改,修改是无效的,否则会出现ConcurrentModificationException,因为不管是增强for循环和forEach都是底层的迭代器实现的,而迭代器是一旦生成,过程中的操作会影响迭代器的运行。
-
迭代器
迭代器Iterator,是⼀个接⼝,Collection集合中有⼀个⽅法 iterator() 可以获取这个接⼝的实现类对象。在这个迭代器中,维护了⼀个引⽤,指向集合中的某⼀个元素。默认指向⼀个集合前不存在的元素,可以认为是下标为-1的元素。
迭代器的⼯作原理(重点):循环调用 next() 方法进行向后的元素指向,并返回新的指向的元素。同时,在向后进行遍历的过程中,使用 hasNext() 判断是否还有下⼀个元素可以迭代。
hasNext():判断迭代过程中是否存在下一元素(注意是下一元素而不是当前是否存在元素)
iterator.next():让迭代器向后指向⼀位,并返回这⼀位的元素
在迭代器使⽤的过程中,需要注意:
- 不允许对集合中的元素进行修改
- 不允许对集合的⻓度进⾏修改
// 1、获取迭代器对象,这⾥的 iterator 是⼀个 Iterator 接⼝的实现类对象 Iterator<String> iterator = collection.iterator(); // 2、使⽤迭代器进⾏集合的遍历 while (iterator.hasNext()) { // 让迭代器向后指向⼀位,并返回这⼀位的元素 String element = iterator.next(); System.out.println(element); }
简单遍历方法(参数是lambda表达式):
//collection.forEach(ele-> System.out.println(ele)); collection.forEach(System.out::println);
11.4 List接口
List接口是Collection的子接口,继承其一切方法并添加了诸多新方法。
List集合的存储特点
11.4.1 List集合的存储特点
List 集合是Collection接⼝的⼀个子接⼝,继承到了⽗接⼝中所有的⽅法。
- List是一个有序的集合:元素添加的顺序和存储的顺序是一致的,因此在LIst集合中存在下标。
- List集合中的元素是不去重的(允许重复)。
常⻅的实现类有:ArrayList、LinkedList、Vector、Stack。
ArrayList:是使⽤数组存储数据的容器,查询效率⾼,增删效率低。
LinkedList:是使⽤双向链表存储数据的容器,增删效率⾼,查询效率低。
Vector:是⼀个古⽼的集合,JDK1.0版本时候出现,现在已经被ArrayList和LinkedList替代。
Stack:是⼀个古⽼的集合,JDK1.0版本时候出现,模拟栈结构存储数据。
11.4.2 常用方法
//1.实例化一个ArrayList对象并向上转型为List集合
List<Integer> list = new ArrayList<>();
list.add(0);
list.add(30);
list.add(10);
//2.在当前索引位之后添加元素
list.add(20);
//3.在指定索引位添加元素
list.add(0,20);
//4.在指定索引位添加多个元素()
list.addAll(list);
list.addAll(1,list);
//5.删除索引位的元素(注意:List添加了根据索引值删除的方法,所以根据元素删除需要取元素值,与collection不同)
list.remove(2);
//6.删除元素:根据值删除
list.remove("lily");
list.remove(Integer.valueOf(50));
//7.修改目标索引位的元素
list.set(2, 50);
//8.修改元素:将集合中每个元素都带入apply(T)方法中,用返回值代替当前元素,例:返回(元素X10)
list.replaceAll(t->t*10);
//9.遍历集合,依次将每个元素都带入到方法中,例:依次输出各元素
list.forEach(System.out::println);
//10.截取集合中[fromIndex,toIndex)范围的元素作为新的集合返回
int fromIndex = 1,toIndex = 4;
list.subList(fromIndex,toIndex);
//11.获取某个元素首次出现的下标
list.indexOf(Object o);
//12.获取某元素最后一次出现的下标
list.LastindexOf(Object o);
**Comparator:**用来做对象比较的函数式接口,里面只有一个抽象方法
int compare(T o1,T o2)
11.4.3 用List实现集合排序(重点):
用lambda表达式对Comparable接口进行重写再通过Arrays方法排序:
public class Program3 {
public static void main(String[] args) {
//1.实例化一个List集合
List<Person>list = new ArrayList<>();
//2.在集合中添加若干元素
list.add(new Person("xiaoming", 18, 172));
list.add(new Person("xiaobai", 22, 160));
list.add(new Person("xiaohei", 22, 167));
list.add(new Person("xiaohong", 24, 171));
list.add(new Person("xiaohuang", 17, 186));
list.add(new Person("xiaolv", 68, 145));
list.add(new Person("xiaocheng", 38, 178));
list.add(new Person("xiaozi", 28, 174));
list.add(new Person("xiaolan", 78, 167));
list.add(new Person("xiaohua", 8, 140));
//需求一:按照年龄对集合元素升序排序
//法一:自定义一个排序方法
sort1(list);
//法二:
list.sort((o1,o2)->o1.getAge()-o2.getAge());
//遍历集合
list.forEach(System.out::println);
//需求二:按照身高对集合元素降序排序
list.sort((o1,o2)->o2.getHeight()-o1.getHeight());
//需求三:以年龄升序为第一需求,身高降序为第二需求排序
list.sort((o1,o2)->{
if(o1.getAge()!=o2.getAge()) {return o1.getAge()-o2.getAge();}
return o2.getHeight()-o1.getHeight();
});
list.forEach(System.out::println);
}
//自定义排序方法
private static void sort1(List<Person> list ) {
for(int i = 0;i<list.size()-1;i++) {
int swapIndex = i;
for(int j =i+1;j<list.size();j++) {
if(compare(list.get(swapIndex),list.get(j))){
swapIndex = j;
}
}
if(swapIndex!=i) {
Person temp = list.get(i);
list.set(i,list.get(swapIndex));
list.set(swapIndex, temp);
}
}
}
private static boolean compare(Person p1,Person p2) {
return p1.getAge()>p2.getAge();
}
}
class Person{
private String name;
private int age;
private int height;
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 int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public Person(String name, int age, int height) {
super();
this.name = name;
this.age = age;
this.height = height;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", height=" + height + "]";
}
}
11.4.4 List接口常用实现类:
1.ArrayList:是一个数组实现的集合,增删操作效率低,查询效率高。
2.LinkedList:是一个链表实现的集合,增删操作效率高,查询效率低。
11.4.5 集合的遍历
-
增强for循环
-
forEach方法
-
迭代器
-
下标遍历
-
列表迭代器 ListIterator(重点)
ListIterator是一个List集合特有的列表迭代器,继承自Iterator接口,添加了功能:
- 在迭代过程中使用add、set、remove 对集合元素进行修改**(只能通过迭代器中的方法进行修改)**。
- 可以使用hasPrevious和previous实现倒序迭代。
//实例化一个Listiterator迭代器对象
Listiterator<String> iterator = list.listIteratoe();
// 3、在迭代的过程中,修改集合
while (iterator.hasNext()) {
// 向后指向⼀位,并返回当前指向的元素
String ele = iterator.next();
if (ele.equals("Vertu")) {
// 在迭代的过程中,删除这个元素
iterator.remove();
// 在迭代的过程中,添加⼀个元素(加在当前迭代的这个元素的后⾯)
iterator.add("HTC");
// 在迭代的过程中,对当前迭代的元素进⾏修改
iterator.set("Nokia");//注:如果这之前已经remove了当前元素,就不能再修改或添加了
}
}
- next:先向后挪动一位指向,再获取当前指向的元素
- previous:先获取当前指向的元素,再向前挪动一个指向。
声明及迭代方法,例:
List<Integer> list = new ArrayList<>();
for(int i=0;i<10;i++) {
list.add(i+1);
}
//ListIterator:列表迭代器,是Iterator的子接口
ListIterator<Integer> iterator = list.listIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
//从索引值(包括索引)开始往后赋值
ListIterator<Integer>listIterator = list.listIterator(5);
11.5 Set接口
Set接口是Collection的子接口
11.5.1 Set集合的存储特点:
-
Set集合是无序的:元素添加的顺序与存储的顺序不一致。
注:无序不代表随机,只是按照的存储规则无序。
-
Set集合是去重的:不允许包含重复元素。
Set接口包含Collection接口的所有方法,没有新方法。
11.5.2 Set集合的去重原理
Set去重默认是根据内存地址去重,如果需要根据内容判断去重,则需要重写去重方法。
关于hashCode和equals方法的设计,一般情况下都是与一些属性关联的。
在IDEA中Alt+Insert选择重写equals和hashCode方法选择默认模板并选择参数可以快捷重写去重方法。
重写一个hashSet的去重规则(重点,对于TreeSet无效),例:LinkedhashSet:有序的,因为增加维护了一个位置索引,效率略低于hashSet。
11.5.3 TreeSet的排序(重点)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hlGPpvJQ-1587711142149)(E:\文件夹目录\JAVA\课程基础\2.jpg)]
是Set接口的实现类,同时也是SortedSet接口的实现类,自带排序但必须先规定好排序规则。
TreeSet的底层是树;除了保留Set集合的存储特征外,还有一个重要的特征:数据是排序的。
注意事项:如果需要保留比较规则校验相同的两个元素,则在定义比较规则最好返回非零数,如果不需保留,直接返回0。
排序一:自定义一种排序方式(此题以年龄升序),注:实现类需实现Comparable接口并重写compareTo方法。
使用场合:通用性,频繁使用同一比较规则时。
public class Program2 {
public static void main(String[] args) {
//1.实例化一个TreeSet对象
TreeSet<Person>treeSet = new TreeSet<>();
Collections.addAll(treeSet,
new Person("xiaoming", 18, '男'),
new Person("xiaohong", 20, '女'),
new Person("xiaobai", 14, '男'),
new Person("lilei", 50, '男')
);
treeSet.forEach(System.out::println);
}
}
class Person implements Comparable<Person>{
private String name;
private int age;
private char gender;
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 char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public Person(String name, int age, char gender) {
super();
this.name = name;
this.age = age;
this.gender = gender;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", gender=" + gender + "]";
}
//重写去重原则中的hashCode方法:绝大部分情况下直接用Object.hash方法返回属性值生成的哈希值
@Override
public int hashCode() {
return Objects.hash(name,age,gender);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (gender != other.gender)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
//重写Comparable接口,使得Program2的树排序正常运行
//根据需要返回this 和 o关于某属性的比较结果
@Override
public int compareTo(Person o) {
return this.age-o.age;
}
}
排序二:通过 TreeSet(Comparator comparator) 的构造方法,通过一个Comparator 的实现类对象实例化一个TreeSet,在comparator中,重新实现compare方法,优先于Comparable的比较规则。
使用场合:偶尔用到的与默认比较规则不同时。
public class Program2 {
public static void main(String[] args) {
//1.使用Comparator接口的实例化对象
Comparator<Person>comparator = (o1,o2)->o1.getAge()-o2.getAge();
//2.通过有参构造实例化一个TreeSet对象
TreeSet<Person>treeSet = new TreeSet<>(comparator);
Collections.addAll(treeSet,
new Person("xiaoming", 18, '男'),
new Person("xiaohong", 20, '女'),
new Person("xiaobai", 14, '男'),
new Person("lilei", 50, '男')
);
treeSet.forEach(System.out::println);
}
}
TreeSet的去重规则:根据排序规则(Comparable或者Compartor)得到集合的同时做重复判断(返回值为0时,后存储的元素舍去)。
11.6 Collections工具类
Collections是一个用来操作集合的工具类。
List<String> list = new ArrayList<String>();
//常用方法
//1.向某个集合中批量添加元素
Collections.addAll(list,"xiaoming","liuhong","chenzhen","libai","sunwen");
//2.量的替换元素
Collections.replaceAll(list, 2, 20);
//3.每调用一次,对集合元素随机排列
Collections.shuffle(list); //参数需要是List类型的
//4.使用目标元素填充整个集合
Collections.fill(list, "hello");
//5.将src中的数据拷贝到dest中(dest必须有足够的容量来存储src的数据)
Collections.copy(dest, src);
//6.线程不安全——>线程安全
List<String> synchronizedList= Collections.synchronizedList(list);
// Collections.synchronizedCollection() // 将⼀个线程不安全的Collection集合转成⼀个线程安全的集合
// Collections.synchronizedList() // 将⼀个线程不安全的List集合转成⼀个线程安全的集合
// Collections.synchronizedSet() // 将⼀个线程不安全的Set集合转成⼀个线程安全的集合
// Collections.synchronizedMap() // 将⼀个线程不安全的Map集合转成⼀个线程安全的集合
//以下方法均针对无序集合,例Set等有序集合,不能根据以下方法重新进行排序。
//7.集合倒序
Collections.reverse(list);
//8.1个集合中的最⼤元素,通过元素的Comparable 接⼝中的实现确定⼤⼩规则
int max = Collections.max(list);
//8.2个指定的⼤⼩⽐较规则,找到⼀个集合中的最⼤元素
max = Collections.max(list, Integer::compareTo);
//9.1合集合中的最⼩元素,通过元素的Comparable 接⼝中的实现确定⼤⼩规则
Collections.min(list);
//9.2个指定的⼤⼩⽐较规则,找到⼀个集合中的最⼩元素
Collections.min(list, Integer::compareTo);
//10.个集合中的元素随机打乱
Collections.shuffle(list);
//11.个集合中指定的两个下标的值
Collections.swap(list, 2, 3);
//12.对集合中的元素排序
//12.1按照集合实现的Comparable接口规则进行排序
Collections.sort(list);
//12.2按照指定的排序规则排序
Collections.sort(list, (o1,o2)->o1.compareTo(o2));
Collections.sort(list, (o1,o2)->o1.length-o2.length);
//13.二分查找法,从⼀个集合中查询元素
Collections.binarySearch(list, 10);
11.7 Map接口
Map是一个无序的、存储双列数据的顶级接口。
- Map中的每一个元素都是一个键值对。
- 每个键都是唯一的,若存入已存在的键,则会用新值覆盖原来的值。
- 在Map中也没有下标的概念。
Key(钥匙的Map保持型) | Value(映射的值的类型) |
---|---|
123 | hello |
常用Map方法:
-
构造方法:
//1.通过无参构造实例化对象(如果没有默认的对键的排序规则,则实例化会报错) Map<String,Integer>map = new TreeMap<>(); //2.通过有参构造确定排序依据来实例化对象 //TreeMap(Comparator) Map<String,Integer>map = new TreeMap<>((o1,o2)->o1.length-o2.length);
注意:如果根据键的排序规则,出现了去重现象,此时新值覆盖旧值。
-
增:
// 1、实例化⼀个 Map 接⼝的实现类的对象,并向上转型为 Map 接⼝类型 Map<String, String> map = new HashMap<>(); // 2、增,向集合中添加⼀个键值对 map.put("name", "xiaoming"); map.put("age", "12"); map.put("gender", "male"); // 3、增,如果增的这个键在Map中已经存在,此时会⽤新的值覆盖原来的值 map.put("age", "20"); // 4、增,当这个键不存在的时候,才会增; 返回值是需添加的值。 map.putIfAbsent("age", "30"); // 5、增,从⼀个Map集合中添加键值对,如果两个Map中存在相同的键,则会⽤新值覆盖原来的值 map.putAll(tmp);
-
删:
// 6、删,按照键删除键值对 map.remove("age"); System.out.println(map); // 7、删,删除指定的键值对,只有当键和值都能够匹配上的时候,才会删除 map.remove("name", "xiaobai"); // 8、删,清空所有的键值对 map.clear();
-
改:
// 9、改,通过键,修改值 map.replace("gender", "female"); // 10、改,只有当键和值都能够匹配上,才会修改值 map.replace("gender", "female", "Unknown"); // 11、 /* V apply(K key, V oldValue): 将Map集合中的每⼀个键值对都带⼊到这个⽅法中,⽤返回值替换原来的值 */ // 需求: 将现在所有的成绩后⾯添加上"分" map.replaceAll((k, v) -> { if (k.matches("java|scala|linux|mysql|hadoop")) { return v + "分"; } return v; }); System.out.println(map);
-
查:
// 12、查询(通过键,查询值)(如果不存在这个键,则结果是 null) String value = map.get("java1"); // 13、查询(通过键查询值,如果不存在这个键,会返回默认的值(defaultVaule此时被设为0)) String value2 = map.getOrDefault("java1", "0");
-
其他方法:
// 1、判断集合中是否包含指定的键 map.containsKey("java"); // 2、判断集合中是否包含指定的值 map.containsValue(98); // 3、获取集合中元素的数量(键值对的数量) map.size(); // 4、判断集合是否是⼀个空集合 map.isEmpty(); // 5、获取所有的值 Collection<Integer> values = map.values(); //6.获取所有的键 Set<String> keys = map.keySet();
Map集合的遍历
-
通过 keySet() 遍历
// 2、获取由所有的 Key 组成的 Set 集合 Set<String> keys = map.keySet(); for (String k : keys) { System.out.println(String.format("key = %s, value = %s", k, map.get(k))); } System.out.println("--------------------------");
-
通过 entrySet() 遍历(需要用Map.Entry类型的对象存储单次遍历的一个键值对,以此进行增强for循环)
/* 3、 Map.Entry<K,V> 是 Map 中的⼀个内部接⼝,⽤来描述集合中的⼀个键值对 常⻅⽅法: 1、getKey() 返回这个键值对中的键 2、getValue() 返回这个键值对中的值 3、setValue() 设置这个键值对中的值 entrySet() 返回所有的Entry实现类对象组成的Set集合 */ Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); for (Map.Entry<String, Integer> entry : entrySet) { String key = entry.getKey(); Integer value = entry.getValue(); //遍历时修改值 //entry.setValue(); System.out.println(String.format("key = %s, value = %s", key, value)); } System.out.println("--------------------------");
-
通过 forEach() 遍历
// 4、forEach /* BiConsumer<T, U> 是⼀个函数式接⼝,接⼝中只有⼀个⽅法 void accept(T t, U u) 这⾥,forEach 的逻辑是: 将键值对中的每⼀个键和值都带⼊到 accept ⽅法中 */ map.forEach((k, v) -> System.out.println(String.format("key = %s, value =%s", k, v)));
Map接口常见的实现类
- HashMap:是最常⽤的Map集合,底层实现采⽤的是哈希表,访问速度快。
- LinkedHashMap:底层实现是链表+哈希表,“有序”。
- TreeMap:底层实现是红⿊树,可以对集合中的元素,按照Key进⾏升序排序。排序的依据参考TreeSet,需要让Key对应的类实现 Comparable 接⼝,或者让 TreeMap 通过有参构造进⾏实例化。
HashMap和Hashtable的区别
- HashMap是线程不安全的集合,Hashtable是线程安全的集合。
- HashMap允许 null 键值,Hashtable不允许。
- HashMap是新版本的Map集合,Hashtable是旧版的。HashMap底层的算法效率⽐Hashtable⾼。
- HashMap的⽗类是 AbstractMap,Hashtable的⽗类是 Dictionary。