文章目录
11.1 Java集合框架概述
- 一方面, 面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象 的操作,就要对对象进行存储。另一方面,使用Array存储对象方面具有一些弊 端,而Java 集合就像一种容器,可以动态地把多个对象的引用放入容器中。
- 数组在内存存储方面的特点:
- 数组初始化以后,长度就确定了。
- 数组声明的类型,就决定了进行元素初始化时的类型
- 数组在存储数据方面的弊端:
- 数组初始化以后,长度就不可变了,不便于扩展
- 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。 同时无法直接获取存储元素的个数
- 数组存储的数据是有序的、可以重复的。---->存储数据的特点单一
- Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的 关联数组。
Java 集合可分为 Collection
和 Map
两种体系
- Collection接口:单列数据,定义了存取一组对象的方法的集合
- List:
元素有序、可重复的集合
- Set:
元素无序、不可重复的集合
- List:
- Map接口:
双列数据,保存具有映射关系“key-value对”的集合
11.2 Collection接口
- Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法 既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
- JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List) 实现。
- 在 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都 当成 Object 类型处理;从 JDK 5.0 增加了
泛型
以后,Java 集合可以记住容 器中对象的数据类型。
11.2.1 Collection的对象创建
- 实际上由于Collection是一个接口,并不能创建对象,只能创建其实现类对象
- 创建对象:
Collection<E> col = new 具体实现类<E>() ; // 这里用到的是多态的性质
Collection<String> 集合名 = new ArrayList<>();
Collection<Student> 集合名 = new HashSet<>();
Collection 集合名 = new ArrayList();
: 泛型
作用 : 用来约束集合类元素的数据类型
使用方法 : 创建集合的时候,拿具体的引用数据类型类名替换E即可
注意 :
1. 泛型只能是引用数据类型不可以是基本数据类型
2. 如果不写泛型,泛型默认是Object
3. 等号右边的泛型可以省略,默认和前面一致
11.2.2 Collection接口的方法
- 添加
add(Object obj)
: 添加一个元素- 获取有效元素的个数
int size()
- 清空集合
void clear()
- 是否是空集合
boolean isEmpty()
- 删除
boolean remove(Object obj)
:通过元素的equals方法判断是否是 要删除的那个元素。只会删除找到的第一个元素- 转成对象数组
Object[] toArray()
- 遍历
iterator()
:返回迭代器对象,用于集合遍历
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class Demo1 {
public static void main(String[] args) {
Collection<String> col = new ArrayList<>();
// add()添加方法
col.add("张三") ;
col.add("李四") ;
col.add("王五") ;
col.add("张三") ;
System.out.println("col = " + col);
// size()元素个数
System.out.println("col.size() = " + col.size());
// isEmpty()是否为空
System.out.println("col.isEmpty() = " + col.isEmpty());
col.remove("张三");
System.out.println("col = " + col);
Object[] array = col.toArray();
System.out.println("Arrays.toString(array) = " + Arrays.toString(array));
col.clear();
System.out.println("col = " + col);
}
}
11.2.3 遍历集合
- 转数组 Object[] toArray()
@Test
public void test1() {
Collection<String> col = new ArrayList<>();
col.add("张三") ;
col.add("李四") ;
col.add("王五") ;
col.add("张三") ;
Object[] array = col.toArray();
for (int i = 0; i < array.length; i++) {
System.out.println("array[" + i + "] = " + array[i]);
}
}
- 迭代器对象 Iterator iterator()
Iterator : 迭代器
如何获取迭代器对象 :Iterator<E> 对象名 = 集合对象.iterator();
成员方法 :
boolean hasNext()
: 查看集合中是否有下一个元素
E next()
: 获取集合中的下一个元素
@Test
public void test2() {
Collection<String> col = new ArrayList<>();
col.add("张三") ;
col.add("李四") ;
col.add("王五") ;
col.add("张三") ;
Iterator<String> iterator = col.iterator();
while (iterator.hasNext()) {
System.out.println("iterator.next() = " + iterator.next());
}
}
- 增强for循环(foreach)
增强for循环的底层还是迭代器
格式 :
for(容器元素类型 变量名 : 集合名/数组名){
//操作变量名 就是在操作集合中的元素
}
@Test
public void test3() {
Collection<String> col = new ArrayList<>();
col.add("张三") ;
col.add("李四") ;
col.add("王五") ;
col.add("张三") ;
for (String s : col) {
System.out.println("s = " + s);
}
}
11.3 Iterator迭代器
11.3.1 ConcurrentModificationException并发修改异常
- 问题引出:
当我们在使用集合时,很容易遇到在遍历的过程中对其进行添加、修改、删除的操作
示例:创建一个集合对象,在其中添加四个字符串对象,使用Iterator迭代器遍历,当遍历到元素"李四"时,向list集合中添加一个字符串对象"田七"
import java.util.ArrayList;
import java.util.Iterator;
public class Demo6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张三") ;
list.add("李四") ;
list.add("王五") ;
list.add("赵六") ;
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String name = iterator.next();
System.out.println("name = " + name);
if (name.equals("李四")) {
list.add("田七") ;
}
}
}
}
name = 张三
name = 李四
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at cn.pdsu.edu._list.Demo6.main(Demo6.java:18)
想要了解为什么会出现
ConcurrentModificationException
这个异常,就要先了解迭代器的底层是如何对集合对象进行遍历的
11.3.2 迭代器底层原理
知道了迭代器在遍历集合时是不能修改集合内元素的,那么增强for循环也不能,因为增强for循环底层也是用迭代器实现的。
如何解决这一问题呢?
最好的解决方案就是不使用,使用for循环
import java.util.ArrayList;
public class Demo6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张三") ;
list.add("李四") ;
list.add("王五") ;
list.add("赵六") ;
for (int i = 0; i < list.size(); i++) {
System.out.println("list.get(i) = " + list.get(i));
if (list.get(i).equals("李四")) {
list.add("田七") ;
}
}
System.out.println("list = " + list);
}
}
结果:
list.get(i) = 张三
list.get(i) = 李四
list.get(i) = 王五
list.get(i) = 赵六
list.get(i) = 田七
list = [张三, 李四, 王五, 赵六, 田七]
11.4 List接口
11.4.1 List接口的方法
List<E>接口 : 元素可重复,元素存取有序,元素有索引的单列集合根节点
对象创建 :
//以多态的方式创建
List<E> 集合名 = new List的实现类集合<>();
增删改查四类操作
增 :
boolean add(E e) : 依次添加元素,永远返回true
void add(int index, E element) : 在指定的索引位置插入元素 //插队
删 :
boolean remove(Object obj) : 按照元素值进行删除,返回删除是否成功
void clear() : 清空集合中的所有元素
E remove(int index) : 按照传入的索引值删除元素,返回是被删除的元素
改 :
E set(int index,E e) : 修改指定索引位置上的元素,并返回被修改的元素
查 :
E get(int index) : 获取集合中的指定索引元素
int size() : 获取集合的长度和元素的个数
boolean contains(Object obj) : 判断集合中是否包含传入的元素
boolean isEmpty() : 判断集合是否为空集合
//如果不存在,返回-1
int indexOf(Object o) : 查询o元素第一次出现在集合的索引位置
int lastIndexOf(Object o) :查询o元素最后一次出现在集合的索引位置
遍历方式 :
- 转数组 Object[] toArray()
- 迭代器 Iterator iterator()
- 增强for 集合名.for
- 普通for 集合名.fori
- 列表迭代器 ListIterator listIterator()
11.4.2 ArrayList存储自定义对象类型,五种遍历方式
- 自定义Student类
import java.util.Objects;
public class Student {
private String name ;
private int age ;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.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 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);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 五种遍历集合的方式
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import java.util.ListIterator;
public class Demo3 {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student("张三" , 20)) ;
list.add(new Student("李四" , 21)) ;
list.add(new Student("王五" , 22)) ;
// 五种遍历集合的方式
// 转数组遍历
Object[] array = list.toArray();
for (int i = 0; i < array.length; i++) {
System.out.println("array[i] = " + array[i]);
}
// 迭代器
Iterator<Student> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println("iterator.next() = " + iterator.next());
}
// 增强for循环
for (Student student : list) {
System.out.println("student = " + student);
}
// for循环
for (int i = 0; i < list.size(); i++) {
System.out.println("list.get(i) = " + list.get(i));
}
// listIterator迭代器
ListIterator<Student> lit = list.listIterator();
while (lit.hasNext()) {
System.out.println("lit.next() = " + lit.next());
}
}
}
11.4.3 关于ArrayList集合中删除多个元素
public class Demo4 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张三") ;
list.add("张四") ;
list.add("张五") ;
list.add("赵四") ;
list.add("王五") ;
list.add("李力") ;
// 倒序删除
for (int i = list.size() - 1; i >= 0; i--) {
if (list.get(i).startsWith("张")) {
list.remove(i) ;
}
}
// list = [赵四, 王五, 李力]
System.out.println("list = " + list);
}
public static void method2(ArrayList<String> list) {
for (int i = 0; i < list.size(); i++) {
if (list.get(i).startsWith("张")) {
list.remove(i) ;
i -- ; // 集合是一个长度变化的容器,删除成功后,list.size()长度减一
}
}
// list = [赵四, 王五, 李力]
System.out.println("list = " + list);
}
public static void method1(ArrayList<String> list) {
for (int i = 0; i < list.size(); i++) {
if (list.get(i).startsWith("张")) {
list.remove(list.get(i)) ;
list.remove(i) ;
}
}
// list = [张四, 赵四, 王五, 李力]
System.out.println("list = " + list);
}
}
11.4.4 关于ArrayList集合中Integer类型元素的删除
public class Demo5 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1) ;
list.add(2) ;
list.add(3) ;
list.add(4) ;
list.add(5) ;
int num = 2 ;
// 按索引删除 删除的是索引为2位置的元素
list.remove(num) ;
// 按元素值删除 删除的是元素值为2的元素
list.remove(Integer.valueOf(num)) ;
System.out.println("list = " + list);
}
}
- 这里是因为Java中调用方法的最佳适配,集合中的
remove()
方法是一个重载的方法,boolean remove(Object obj)
E remove(int index)
,当我们传入int
类型的num时,系统会自动匹配到按照索引删除的方法,可以直接使用num来进行方法的执行,这样是最方便的。如果匹配到按照元素删除的方法,int类型的num需要先进行自动装箱的操作,将int类型转为Integer类型
,然后再执行remove()方法。反过来也是这样,传入的是Integer类型,就会优先匹配boolean remove(Object obj)方法
11.4.5 ListIterator列表迭代器
ListIterator<E>列表迭代器 : List集合下特有的迭代器对象
ListIterator<E>列表迭代器接口 是 Iterator<E>接口的子接口,功能更为强大.但是它属于List专属
创建列表迭代器对象的方式 :
1. List集合名.listIterator();
2. List集合名.listIterator(索引); 指定列表迭代器对象的开始位置
方法 :
boolean hasNext() : 判断指针右边是否有下一个元素
E next() : (先校验镜像和原集合的内容是否一致)获取指针右边的元素并移动指针到下一个可停留位置
void add(E e) : 列表迭代器添加元素的方法 (先往镜像中添加元素,立刻同步回原集合)
void set(E e) : 修改指针指向的那个索引元素(先修改镜像,再同步到原集合)
使用这个迭代器也能避免并发修改异常问题
11.4.6 ArrayList集合
ArrayList<E>集合 数据结构是数组结构的List集合的实现; -> 最常用的元素可重复的单列集合
//集合命名 : 数据类型 + 集合类型
对象创建 :
ArrayList<E> list = new ArrayList<>();
增删改查四类操作
增 :
boolean add(E e) : 依次添加元素,永远返回true
void add(int index, E element) : 在指定的索引位置插入元素 //插队
删 :
boolean remove(Object obj) : 按照元素值进行删除,返回删除是否成功
void clear() : 清空集合中的所有元素
E remove(int index) : 按照传入的索引值删除元素,返回是被删除的元素
改 :
E set(int index,E e) : 修改指定索引位置上的元素,并返回被修改的元素
查 :
E get(int index) : 获取集合中的指定索引元素
int size() : 获取集合的长度和元素的个数
boolean contains(Object obj) : 判断集合中是否包含传入的元素
boolean isEmpty() : 判断集合是否为空集合
//如果不存在,返回-1
int indexOf(Object o) : 查询o元素第一次出现在集合的索引位置
int lastIndexOf(Object o) :查询o元素最后一次出现在集合的索引位置
遍历方式 :
1. 转数组 Object[] toArray()
2. 迭代器 Iterator iterator()
3. 增强for 集合名.for -> 不修改集合内容时需要遍历推荐增强for
4. 普通for 集合名.fori -> 推荐
5. 列表迭代器 ListIterator listIterator()
方法同List的方法相同
11.4.7 ArrayList的底层扩容原理
- 结论:
1、当创建一个ArrayList集合时,底层会先创建一个长度为0的Object数组
2、当第一次向集合中添加元素时,源码会创建一个长度为10的新数组替换长度为0的旧数组(第一次扩容)
3、当集合添加到第十一个元素时,容积不够,源码会再创建一个长度为15的新数组替换长度为10的老数组(第二次扩容)
4、每当集合底层中的数组元素存满了,源码就会创建一个长度为 (老数组长度 * 1.5) 长度的新数组
newCapacity = oldCapacity + (oldCapacity >> 1)
5、hugeCapacity()方法的源码 :
(minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
a 如果此时集合底层数组的长度+1 没有超过 Integer.MAX_VALUE - 8 , 那么扩容后的集合底层数组长度是MAX_ARRAY_SIZE
b 如果此时集合底层数组的长度+1 超过 Integer.MAX_VALUE - 8 , 那么扩容后的集合底层数组长度是 Integer.MAX_VALUE
当向ArrayList集合中添加第1024个元素值时,此时集合数组的长度为1234。(前提是使用无参构造创建的集合对象)
- ArrayList的JDK1.8之前与之后的实现区别?
- JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
- JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元 素时再创建一个始容量为10的数组
11.4.8 LinkedList集合
LinkedList<E>集合 : 底层数据结构是双向链表结构
源码验证 :
class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
创建对象的格式,增删改查,遍历方式 和 ArrayList 一致 //链表结构是没有索引的,但是LinkedList集合有索引
对象创建 :
LinkedList<E> list = new LinkedList<>();
增删改查四类操作
增 :
boolean add(E e) : 依次添加元素,永远返回true
void add(int index, E element) : 在指定的索引位置插入元素 //插队
删 :
boolean remove(Object obj) : 按照元素值进行删除,返回删除是否成功
void clear() : 清空集合中的所有元素
E remove(int index) : 按照传入的索引值删除元素,返回是被删除的元素
改 :
E set(int index,E e) : 修改指定索引位置上的元素,并返回被修改的元素
查 :
E get(int index) : 获取集合中的指定索引元素
int size() : 获取集合的长度和元素的个数
boolean contains(Object obj) : 判断集合中是否包含传入的元素
boolean isEmpty() : 判断集合是否为空集合
//如果不存在,返回-1
int indexOf(Object o) : 查询o元素第一次出现在集合的索引位置
int lastIndexOf(Object o) :查询o元素最后一次出现在集合的索引位置
遍历方式 :
1. 转数组 Object[] toArray()
2. 迭代器 Iterator iterator()
3. 增强for 集合名.for -> 不修改集合内容时需要遍历推荐增强for
4. 普通for 集合名.fori -> 推荐
5. 列表迭代器 ListIterator listIterator()
LinkedList的特殊方法 : 一些关于头尾操作的方法
void addFirst(E e)
void addLast(E e)
E getFirst()
E getLast()
E removeFirst()
E removeLast()
方法示例:
import java.util.LinkedList;
public class Demo8 {
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>() ;
// LinkedList的add()方法
linkedList.add("张三") ;
linkedList.add("李四") ;
linkedList.add("王五") ;
System.out.println("linkedList = " + linkedList); // linkedList = [张三, 李四, 王五]
// addFirst()方法,头插
linkedList.addFirst("我是头");
// addLast()方法,尾插
linkedList.addLast("我是尾");
System.out.println("linkedList = " + linkedList); // linkedList = [我是头, 张三, 李四, 王五, 我是尾]
// getFirst()得到头元素
System.out.println("linkedList.getFirst() = " + linkedList.getFirst()); // linkedList.getFirst() = 我是头
// getLast()得到尾元素
System.out.println("linkedList.getLast() = " + linkedList.getLast()); // linkedList.getLast() = 我是尾
// removeFirst()删除头
System.out.println("linkedList.removeFirst() = " + linkedList.removeFirst()); // linkedList.removeFirst() = 我是头
// 删除尾
System.out.println("linkedList.removeLast() = " + linkedList.removeLast()); // linkedList.removeLast() = 我是尾
System.out.println("linkedList = " + linkedList); // linkedList = [张三, 李四, 王五]
}
}
- 对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高
11.4.9 Vector集合
-
Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList 相同,区别之处在于Vector是线程安全的。
-
在各种list中,最好把ArrayList作为首选。当插入、删除频繁时, 使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。
- 新增方法:
void addElement(Object obj)
void insertElementAt(Object obj,int index)
void setElementAt(Object obj,int index)
void removeElement(Object obj)
void removeAllElements()
11.4.10ArrayList/LinkedList/Vector的异同
请问ArrayList/LinkedList/Vector的异同?谈谈你的理解?ArrayList底层 是什么?扩容机制?Vector和ArrayList的最大区别?
-
ArrayList和LinkedList的异同 二者都线程不安全,相对线程安全的Vector,执行效率高。 此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于随机访问get和set,ArrayList绝对优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。
-
ArrayList和Vector的区别Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用 ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。
11.5 Set接口
-
Set接口是Collection的子接口,set接口没有提供额外的方法
-
Set接口的特点 : 元素唯一,元素存取无序。Set集合是元素无索引的单列集合根节点
-
Set集合的使用
因为Set是一个接口类型,无法直接创建对象,需要利用多态的性质。
创建对象:Set<E> set = new 具体实现类<>();
增 :
boolean add(E e)
: 往集合中添加元素,返回添加是否成功
删 :
boolean remove(Object o)
: 按照传入的元素删除集合中的该元素
void clear()
: 清空集合中所有的内容改 : 因为没有索引,所以没有set方法
查 : 因为没有索引,所以没有get方法
int size()
: 集合元素个数/集合的长度
boolean contains(Object o)
: 是否包含
boolean isEmpty()
: 是否为空遍历
1. 转数组
2. 迭代器
1. 增强for循环
示例代码:
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Demo2 {
public static void main(String[] args) {
// 创建对象
// Set<E> 集合名 = new 具体的实现类对象<>();
Set<String> set = new HashSet<>() ;
System.out.println("set = " + set);
// 增删改查四类功能
// 增 :
// boolean add(E e) : 往集合中添加元素,返回添加是否成功
boolean b = set.add("张三");
set.add("李四") ;
set.add("王五") ;
set.add(null) ;
boolean b1 = set.add("张三");
System.out.println("b = " + b);
System.out.println("b1 = " + b1);
System.out.println("set = " + set);
// 删 :
// boolean remove(Object o) : 按照传入的元素删除集合中的该元素
boolean b2 = set.remove("王五");
System.out.println("b2 = " + b2);
System.out.println("set = " + set);
// 改 : 因为没有索引,所以没有set方法
// 查 : 因为没有索引,所以没有get方法
// int size() : 集合元素个数/集合的长度
int size = set.size();
System.out.println("size = " + size);
//
// boolean contains(Object o) : 是否包含
boolean b3 = set.contains("张三");
System.out.println("b3 = " + b3);
// boolean isEmpty() : 是否为空
boolean b4 = set.isEmpty();
System.out.println("b4 = " + b4);
// 遍历
// 1. 转数组
System.out.println("-----------------------");
Object[] array = set.toArray();
for (int i = 0; i < array.length; i++) {
System.out.println("array[i] = " + array[i]);
}
// 2. 迭代器
System.out.println("-----------------------");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
System.out.println("next = " + next);
}
// 3. 增强for循环
System.out.println("-----------------------");
for (String s : set) {
System.out.println("s = " + s);
}
// void clear() : 清空集合中所有的内容
set.clear();
System.out.println("set = " + set);
}
}
set = []
b = true
b1 = false
set = [null, 李四, 张三, 王五]
b2 = true
set = [null, 李四, 张三]
size = 3
b3 = true
b4 = false
-----------------------
array[i] = null
array[i] = 李四
array[i] = 张三
-----------------------
next = null
next = 李四
next = 张三
-----------------------
s = null
s = 李四
s = 张三
set = []
11.5.2 Set实现类之一:HashSet集合
- HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
- HashSet
按 Hash 算法来存储集合中的元素
,因此具有很好的存取、查找、删除 性能。 - HashSet 具有以下特点:
- 不能保证元素的排列顺序
- HashSet 不是线程安全的
- 集合元素可以是
null
- HashSet 集合判断两个元素相等的标准:两个对象通过
hashCode()
方法比较相 等,并且两个对象的equals()
方法返回值也相等。 - 对于存放在Set容器中的对象,对应的类
一定要重写equals()和hashCode(Object obj)方法
,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。
创建对象
HashSet<E> 集合名 = new HashSet<>();
增删改查四类功能
增 :
boolean add(E e)
: 往集合中添加元素,返回添加是否成功
删 :
boolean remove(Object o)
: 按照传入的元素删除集合中的该元素
void clear()
: 清空集合中所有的内容
改 : 因为没有索引,所以没有set方法
查 : 因为没有索引,所以没有get方法
int size()
: 集合元素个数/集合的长度
boolean contains(Object o)
: 是否包含
boolean isEmpty()
: 是否为空
遍历
- 转数组
- 迭代器
- 增强for循环
-向HashSet集合中添加元素的过程:
- 当向HashSet集合中添加元素时,HashSet集合会
调用该对象的HashCode()方法来得到它的HashCode值
,然后根据HashCode值再通过某种散列函数决定该元素在HashSet底层数组中的存储位置(这个散列函数会与底层数组的长度相计算得到在 数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布, 该散列函数设计的越好)- 如果两个元素的HashCode值相等,会再去调用
equals()
方法,如果equals()方法的结果为true,则添加失败;如果equals()结果为false,则会保存该元素
。该数组的位置已经有元素了, 那么会通过链表的方式继续链接。
- 哈希表:
- 重写 hashCode() 方法的基本原则
- 在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
- 当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等。
- 对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
- 重写 equals() 方法的基本原则
- 当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是 要改写hashCode(),根据一个类的equals方法(改写后),两个截然不 同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法, 它们仅仅是两个对象。
- 因此,违反了“相等的对象必须具有相等的散列码”。
- 结论:复写equals方法的时候一般都需要同时复写hashCode方法。通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。
11.5.3 Set实现类之二:LinkedHashSet
- LinkedHashSet 是 HashSet 的子类
- LinkedHashSet
根据元素的 hashCode 值来决定元素的存储位置
, 但它同时使用双向链表维护元素的次序
,这使得元素看起来是以插入顺序保存的。 - LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全 部元素时有很好的性能。
LinkedHashSet 不允许集合元素重复
。
import java.util.Iterator;
import java.util.LinkedHashSet;
public class Demo3 {
public static void main(String[] args) {
// 创建对象
LinkedHashSet<String> set = new LinkedHashSet<>() ;
System.out.println("set = " + set);
// 增删改查四类功能
// 增 :
// boolean add(E e) : 往集合中添加元素,返回添加是否成功
boolean b = set.add("张三");
set.add("李四") ;
set.add("王五") ;
set.add(null) ;
boolean b1 = set.add("张三");
System.out.println("b = " + b);
System.out.println("b1 = " + b1);
System.out.println("set = " + set);
// 删 :
// boolean remove(Object o) : 按照传入的元素删除集合中的该元素
boolean b2 = set.remove("王五");
System.out.println("b2 = " + b2);
System.out.println("set = " + set);
// 改 : 因为没有索引,所以没有set方法
// 查 : 因为没有索引,所以没有get方法
// int size() : 集合元素个数/集合的长度
int size = set.size();
System.out.println("size = " + size);
// boolean contains(Object o) : 是否包含
boolean b3 = set.contains("张三");
System.out.println("b3 = " + b3);
// boolean isEmpty() : 是否为空
boolean b4 = set.isEmpty();
System.out.println("b4 = " + b4);
// 遍历
// 1. 转数组
System.out.println("-----------------------");
Object[] array = set.toArray();
for (int i = 0; i < array.length; i++) {
System.out.println("array[i] = " + array[i]);
}
// 2. 迭代器
System.out.println("-----------------------");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
System.out.println("next = " + next);
}
// 3. 增强for循环
System.out.println("-----------------------");
for (String s : set) {
System.out.println("s = " + s);
}
// void clear() : 清空集合中所有的内容
set.clear();
System.out.println("set = " + set);
}
}
set = []
b = true
b1 = false
set = [张三, 李四, 王五, null]
b2 = true
set = [张三, 李四, null]
size = 3
b3 = true
b4 = false
-----------------------
array[i] = 张三
array[i] = 李四
array[i] = null
-----------------------
next = 张三
next = 李四
next = null
-----------------------
s = 张三
s = 李四
s = null
set = []
11.5.4 Set实现类之三:TreeSet
11.5.4.1 树结构
11.5.4.2 TreeSet集合
- TreeSet集合 : 底层数据结构是红黑树结构的Set集合
- 特点 : 元素唯一,元素存取无序,无索引,元素不能为null
创建对象/增删改查/遍历 和 Set 接口中的功能一致的!
- TreeSet集合可以对元素进行排序,但是排序的规则由程序员提供
常用类型的默认排序规则:
Integer : 默认升序 (由源码提供)
String : 默认升序(字符串中字符的ASCII码值的升序 ) //越靠前的字符ASCII值的优先级越高 (“10” 会排在在 “2” 字符串的前面)自定义类型的元素存储在TreeSet集合中的排序规则 : 如果元素的类型没有提供排序规则,那么直接报错 (添加不进去)
import java.util.TreeSet;
public class Demo4 {
public static void main(String[] args) {
TreeSet<String> set = new TreeSet<>() ;
System.out.println("set = " + set);
set.add("王五") ;
set.add("张三");
set.add("李四") ;
set.add("张三");
System.out.println("set = " + set);
}
}
set = []
set = [张三, 李四, 王五]
public class Demo4 {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>() ;
System.out.println("set = " + set);
set.add(55) ;
set.add(100);
set.add(3) ;
set.add(10);
set.add(80);
System.out.println("set = " + set);
}
}
set = []
set = [3, 10, 55, 80, 100]
自定义Student类,没有实现Comparable接口
public class Demo4 {
public static void main(String[] args) {
TreeSet<Student> set = new TreeSet<>() ;
set.add(new Student("张三" , 19)) ;
set.add(new Student("李四" , 20));
set.add(new Student("王五" , 21)) ;
System.out.println("set = " + set);
}
}
Exception in thread “main”
java.lang.ClassCastException:
cn.pdsu.edu._set.Student cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1294)
at java.util.TreeMap.put(TreeMap.java:538)
at java.util.TreeSet.add(TreeSet.java:255)
at cn.pdsu.edu._set.Demo4.main(Demo4.java:12)
11.5.5 比较器
11.5.5.1 Comparable比较器
-
当我们向 TreeSet 中添加元素时,只有第一个元素无须调用compareTo()方法进行比较,后面添加的所有元素都会调用compareTo()方法进行比较。
-
因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同 一个类的对象。
-
对于 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj) 方法比较返回值。
-
当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与 compareTo(Object obj) 方法有一致的结果:如果两个对象通过 equals() 方法比较返回 true,则通过 compareTo(Object obj) 方法比较应返回0。
对于Integer和String类,它们都有实现Comparable接口,都重写了compareto方法,所以在我们使用TreeSet集合存储Integer或String类型的数据时,它们都会进行一个排序从小到大的排序(升序)。
import java.util.TreeSet;
public class Demo2 {
public static void main(String[] args) {
// 创建TreeSet集合存储Integer/String类型的元素,查看其存储的方式
TreeSet<Integer> treeSet = new TreeSet<>();
// 乱序添加数据
treeSet.add(5) ;
treeSet.add(2) ;
treeSet.add(4) ;
treeSet.add(1) ;
treeSet.add(3) ;
// 输出集合查看集合内元素的数据
// treeSet = [1, 2, 3, 4, 5]
// 对于Integer类型的数据,在TreeSet中就是根据数值的大小进行排序的
System.out.println("treeSet = " + treeSet);
TreeSet<String> treeSet1 = new TreeSet<>() ;
// 添加String类型的数据
treeSet1.add("123") ;
treeSet1.add("ABC") ;
treeSet1.add("abc") ;
treeSet1.add("wer") ;
treeSet1.add("888") ;
// 输出集合内数据查看排序后的顺序
// treeSet1 = [123, 888, ABC, abc, wer]
// 对于字符串类型的数据,在TreeSet中是按照Ascall码表进行排序的,
// 对于中文ascall表中虽然没有,但是在其他的编码中也有中文对应的编码值
System.out.println("treeSet1 = " + treeSet1);
}
}
- 对于没有实现Comparable接口的类来说,是没法将元素存入进TreeSet集合的。
自定义Student类
import java.util.Objects;
public class Student {
private String name ;
private int age ;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/** 省略set/get方法*/
@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);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
尝试将自定义类Student对象作为元素存入
import java.util.TreeSet;
public class Demo {
public static void main(String[] args) {
// 创建TreeSer集合
TreeSet<Student> set = new TreeSet<>();
// 向集合中存入元素
set.add(new Student("张三" , 19)) ;
set.add(new Student("李四" , 20)) ;
set.add(new Student("王五" , 21)) ;
System.out.println("set = " + set);
}
}
Exception in thread “main”
java.lang.ClassCastException:
cn.pdsu.edu._test.Student cannot be cast to java.lang.Comparable
at java.util.TreeMap.compare(TreeMap.java:1294)
at java.util.TreeMap.put(TreeMap.java:538)
at java.util.TreeSet.add(TreeSet.java:255)
at cn.pdsu.edu._test.Demo.main(Demo.java:11)
因为Student类没有实现Comparable接口,在进行类型转换时就直接报错了,所以会有一个java.lang.ClassCastException
- Student类实现Comparable接口并重写compareTo()方法
import java.util.Objects;
public class Student implements Comparable<Student>{
......
/**
* 实现Comparable接口中的compareTo()方法
* @param o 这里的o表示的是集合中存在的老元素,调用compareTo()方法的元素是新元素
* @return 返回一个int类型的整数,0表示相等,正数表示新元素大于老元素,负数表示新元素小于老元素
*/
@Override
public int compareTo(Student o) {
// 先按年龄进行比较,this - o表示升序
if (this.age - o.age != 0) return this.age - o.age ;
// 当年龄相等时按照字符串的排序规则返回
return this.name.compareTo(o.name);
}
}
import java.util.TreeSet;
public class Demo {
public static void main(String[] args) {
// 创建TreeSer集合
TreeSet<Student> set = new TreeSet<>();
// 向集合中存入元素
set.add(new Student("张三" , 19)) ;
set.add(new Student("李四" , 20)) ;
set.add(new Student("王五" , 21)) ;
// set = [Student{name='张三', age=19}, Student{name='李四', age=20}, Student{name='王五', age=21}]
// 年龄升序
System.out.println("set = " + set);
}
}
11.5.5.2 Comparator比较器
-
对于Comparable比较器来说,我们需要实现Comparable接口并重写其抽象方法,类与接口之间有较强的耦合性。且对于Integer类和String类来说,它们的排序规则是无法改变的,因为无法继承这两个类对其
compareTo()方法
进行重写,所以有些不灵活。这时候我们就可以考虑使用Comparator比较器来对对象元素编写一套排序规则。 -
Comparator比较器 : 独立存在的比较器,给容器提供排序规则. 容器内的元素按照容器给的排序规则进行排序Comparator比较器的优先级比Comparable比较器高;
修改Integer和String的排序规则
import java.util.Comparator;
import java.util.TreeSet;
public class Demo1 {
public static void main(String[] args) {
// 使用Comparator比较器匿名内部类,实现compare()方法,对Integer类型的元素进行降序排序
TreeSet<Integer> treeSet = new TreeSet<>(new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
treeSet.add(5) ;
treeSet.add(2) ;
treeSet.add(4) ;
treeSet.add(1) ;
treeSet.add(3) ;
// treeSet = [5, 4, 3, 2, 1] 降序排列
System.out.println("treeSet = " + treeSet);
TreeSet<String> set = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
}) ;
set.add("123") ;
set.add("ABC") ;
set.add("abc") ;
set.add("wer") ;
set.add("888") ;
// set = [wer, abc, ABC, 888, 123] 降序排列
System.out.println("set = " + set);
}
}
Student的排序规则
import java.util.Comparator;
import java.util.TreeSet;
public class Demo {
public static void main(String[] args) {
// 创建TreeSer集合
TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o2.getAge() - o1.getAge() ;
}
});
// 向集合中存入元素
set.add(new Student("张三" , 19)) ;
set.add(new Student("李四" , 20)) ;
set.add(new Student("王五" , 21)) ;
// set = [Student{name='王五', age=21}, Student{name='李四', age=20}, Student{name='张三', age=19}]
// 年龄降序
System.out.println("set = " + set);
}
}
11.5.6 Set集合中的两个注意点
import java.util.HashSet;
public class Demo5 {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
/**
* 在HashSet集合中存储数据时,是按照hash值来确定位置的,在存储p1时,是按照 1001,“AA”来计算hash值的
* 而在下面对其内容进行了修改,当调用remove()方法删除p1元素时,需要先计算hash值,而此时hash值的计算是按照1001,“CC”来的
* 但是并不能找到以1001,“AA”计算的hash值,所以删除失败,p1仍存在
*
* 而在存储下面的 1001,“CC” 时,hash值是按1001,“CC”计算的,虽然存在相同的元素内容,但是根本不会调用到equals()方法
* 存储成功,而存储 1001,“AA”时,equals()方法比较结果为false,存储成功
*/
Person p1 = new Person(1001,"AA");
Person p2 = new Person(1002,"BB");
set.add(p1);
set.add(p2);
p1.name = "CC";
boolean remove = set.remove(p1);
// System.out.println("remove = " + remove); // false
System.out.println(set);
set.add(new Person(1001,"CC"));
System.out.println(set);
set.add(new Person(1001,"AA"));
System.out.println(set);
/**
* [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
* [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
* [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}, Person{id=1001, name='AA'}]
*/
}
}
去重
使用Set集合对list集合中的元素进行去重
import java.util.ArrayList;
import java.util.HashSet;
public class Demo6 {
public static void main(String[] args) {
// 去除List集合中的重复数值
ArrayList<Integer> list = new ArrayList<>();
list.add(5) ;
list.add(5) ;
list.add(1) ;
list.add(1) ;
list.add(2) ;
HashSet<Integer> hashSet = new HashSet<>();
hashSet.addAll(list);
ArrayList<Integer> list1 = new ArrayList<>();
list1.addAll(hashSet) ;
System.out.println("list1 = " + list1);
}
}
list1 = [1, 2, 5]
11.6 Map接口
11.6.1 Map接口继承树
11.6.2 Map接口概述
Map与Collection并列存在。用于保存具有映射关系的数据:key-value
Map 中的 key 和 value 都可以是任何引用类型的数据
Map 中的 key 用Set来存放,不允许重复,即同一个 Map 对象所对应 的类,须重写hashCode()和equals()方法 - 常用String类作为Map的“键”
key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到 唯一的、确定的 value
Map接口的常用实现类:
HashMap、TreeMap、LinkedHashMap
和Properties
。其中,HashMap是 Map 接口使用频率最高的实现类
- 添加、删除、修改操作:
Object put(Object key,Object value)
:将指定key-value添加到(或修改)当前map对象中void putAll(Map m):
将m中的所有key-value对存放到当前map中Object remove(Object key)
:移除指定key的key-value对,并返回valuevoid clear()
:清空当前map中的所有数据- 元素查询的操作:
Object get(Object key)
:获取指定key对应的valueboolean containsKey(Object key)
:是否包含指定的keyboolean containsValue(Object value)
:是否包含指定的valueint size()
:返回map中key-value对的个数boolean isEmpty()
:判断当前map是否为空boolean equals(Object obj)
:判断当前map和参数对象obj是否相等- 元视图操作的方法:
Set keySet()
:返回所有key构成的Set集合Collection values()
:返回所有value构成的Collection集合Set entrySet()
:返回所有key-value对构成的Set集合
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Demo2 {
public static void main(String[] args) {
// 创建Map集合的实现类对象
Map<String , String> map = new HashMap<>() ;
System.out.println("map = " + map);
System.out.println("----------------------");
// 添加元素
map.put("key1" , "value1") ;
map.put("key2" , "value2") ;
map.put("key3" , "value3") ;
map.put("key4" , "value4") ;
map.put("key5" , "value5") ;
System.out.println("map = " + map);
System.out.println("----------------------");
// 修改元素
// 修改元素和添加元素都是使用的put()方法
map.put("key1" , "修改后") ;
map.put("key3" , "修改后") ;
System.out.println("map = " + map);
System.out.println("----------------------");
// 查找元素
String key1 = map.get("key1");
String key2 = map.get("key2");
System.out.println("key1 = " + key1);
System.out.println("key2 = " + key2);
System.out.println("----------------------");
// 删除元素
map.remove("key1") ;
map.remove("key3") ;
System.out.println("map = " + map);
System.out.println("----------------------");
// 查找Map集合中是否存在指定的key/value
boolean b = map.containsKey("key2");
boolean b1 = map.containsValue("value2");
System.out.println("b = " + b);
System.out.println("b1 = " + b1);
System.out.println("----------------------");
// 判断当前map集合是否为空
boolean empty = map.isEmpty();
System.out.println("empty = " + empty);
System.out.println("----------------------");
// 返回当前map集合中key-value的个数
int size = map.size();
System.out.println("size = " + size);
System.out.println("----------------------");
// 返回所有key构成的set集合
Set<String> set1 = map.keySet();
System.out.println("set1 = " + set1);
System.out.println("----------------------");
// 返回所有value构成的set集合
Collection<String> set2 = map.values();
System.out.println("set2 = " + set2);
System.out.println("----------------------");
// 返回所有key-value对构成的Set集合
Set<Map.Entry<String, String>> entries = map.entrySet();
System.out.println("entries = " + entries);
}
}
map = {}
----------------------
map = {key1=value1, key2=value2, key5=value5, key3=value3, key4=value4}
----------------------
map = {key1=修改后, key2=value2, key5=value5, key3=修改后, key4=value4}
----------------------
key1 = 修改后
key2 = value2
----------------------
map = {key2=value2, key5=value5, key4=value4}
----------------------
b = true
b1 = true
----------------------
empty = false
----------------------
size = 3
----------------------
set1 = [key2, key5, key4]
----------------------
set2 = [value2, value5, value4]
----------------------
entries = [key2=value2, key5=value5, key4=value4]
11.6.3 Map实现类之一:HashMap
11.6.3.1 HashMap的特点
- HashMap是 Map 接口使用频率最高的实现类。
- 允许使用null键和null值,与HashSet一样,不保证映射的顺序。
- 所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写: equals()和hashCode()
- 所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类 要重写:equals()
- 一个key-value构成一个entry
- 所有的entry构成的集合是Set:无序的、不可重复的
- HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true, hashCode 值也相等。
- HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。
11.6.3.2 HashMap的存储结构
- JDK 7及以前版本:HashMap是数组+链表结构(即为链地址法)
- JDK 8版本发布以后:HashMap是数组+链表+红黑树实现。
11.6.3.3 HashMap集合的方法
HashMap实现了Map接口的方法
- 创建对象
HashMap<键集元素类型,值集元素类型> 集合名 = new HashMap<>();
- 增删改查
-
增 / 改 :
V put(K key,V value)
: 添加一对映射关系,添加成功返回null; 添加失败返回oldValue
-
删 :
V remove(K key)
: 根据传入的键删除集合中的这一对映射关系,并返回这对映射关系的值boolean remove(K key,V value)
: 根据传入的键和值删除集合中的一对元素,返回删除是否成功void clear()
: 清空集合中所有的映射关系
-
查 :
-
V get(K key)
: 根据传入的键获取此键对应的值 ; -
int size()
: 返回集合中有几对元素 -
boolean containsKey(K key)
: 判断是否存在传入的键 -
boolean containsValue(V value)
: 判断是否存在传入的值 -
Set<K> keySet()
: 获取双列集合的键集 -
Collection<V> values()
: 获取双列集合的值集
-
-
- 遍历
Set<Map.Entry<K,V>> entrySet()
:获取到HashMap集合中的所有Entry对象,从Entry对象中得到key和value值de- 通过
keySet()
方法先获取Map集合中的所有key集,然后根据key来获取value
public class Demo3 {
public static void main(String[] args) {
// 创建HashMap对象
Hashtable<String, String> map = new Hashtable<>();
// 添加元素
map.put("张三" , "男") ;
map.put("李四" , "男") ;
map.put("王五" , "女") ;
map.put("赵六" , "女") ;
System.out.println("map = " + map);
System.out.println("==========================");
// 修改元素
map.put("张三" , "女") ;
System.out.println("map = " + map);
System.out.println("==========================");
// 删除元素
map.remove("张三") ;
System.out.println("map = " + map);
System.out.println("==========================");
// 查找元素
String s = map.get("李四");
System.out.println("s = " + s);
System.out.println("==========================");
// 返回集合中存在几对元素
int size = map.size();
System.out.println("size = " + size);
System.out.println("==========================");
// 判断key/value是否在集合中存在
boolean b = map.containsKey("李四");
boolean b1 = map.containsValue("女");
System.out.println("b = " + b);
System.out.println("b1 = " + b1);
System.out.println("==========================");
// 获取Map集合中的键集
Set<String> keys = map.keySet();
System.out.println("keys = " + keys);
System.out.println("==========================");
// 获取Map集合中的值集
Collection<String> values = map.values();
System.out.println("values = " + values);
}
}
map = {赵六=女, 王五=女, 张三=男, 李四=男}
==========================
map = {赵六=女, 王五=女, 张三=女, 李四=男}
==========================
map = {赵六=女, 王五=女, 李四=男}
==========================
s = 男
==========================
size = 3
==========================
b = true
b1 = true
==========================
keys = [赵六, 王五, 李四]
==========================
values = [女, 女, 男]
- 遍历Map集合内的元素
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
public class Demo4 {
public static void main(String[] args) {
// 创建HashMap对象
Hashtable<String, String> map = new Hashtable<>();
// 添加元素
map.put("张三" , "男") ;
map.put("李四" , "男") ;
map.put("王五" , "女") ;
map.put("赵六" , "女") ;
// 遍历Map集合的元素
// 第一种方法:先获取Map集合中的键集,根据键集来获取值集,遍历集合
Set<String> keySet = map.keySet();
for (String key : keySet) {
String value = map.get(key);
System.out.println(key + "-----" + value);
}
System.out.println("-----------------");
// 第二种方法:获取所有的Entry对象,从entry对象中获取key/value值
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "-----" + value);
}
/**
* 赵六-----女
* 王五-----女
* 张三-----男
* 李四-----男
* -----------------
* 赵六-----女
* 王五-----女
* 张三-----男
* 李四-----男
*/
}
}
11.6.4 Map实现类之二:LinkedHashMap
`- LinkedHashMap 是 HashMap 的子类
- 在HashMap存储结构的基础上,使用了一对双向链表来记录添加 元素的顺序
- 与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代 顺序:迭代顺序与 Key-Value 对的插入顺序一致
`
11.6.5 Map实现类之三:TreeMap
- TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序。 TreeMap 可以保证所有的 Key-Value 对处于有序状态。
- TreeSet底层使用红黑树结构存储数据
- TreeMap 的 Key 的排序:
- 自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有 的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
- 定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口
- TreeMap判断两个key相等的标准:两个key通过compareTo()方法或 者compare()方法返回0。
11.6.6 Map实现类之四:Hashtable
- Hashtable是个古老的 Map 实现类,JDK1.0就提供了。不同于HashMap, Hashtable是线程安全的。
- Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询 速度快,很多情况下可以互用。
- 与HashMap不同,Hashtable 不允许使用 null 作为 key 和 value
- 与HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序
- Hashtable判断两个key相等、两个value相等的标准,与HashMap一致。
11.6.7 Map实现类之五:Properties
- Properties 类是 Hashtable 的子类,该对象用于处理属性文件
- 由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key 和 value 都是字符串类型
- 存取数据时,建议使用setProperty(String key,String value)方法和 getProperty(String key)方法
11.7 Collections工具类
- Collections 是一个操作 Set、List 和 Map 等集合的工具类
- Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作, 还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
- 排序操作:(均为static方法)
- reverse(List):反转 List 中元素的顺序
- shuffle(List):对 List 集合元素进行随机排序
- sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
- sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
- swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Demo1 {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("张三") ;
arrayList.add("李四") ;
arrayList.add("王五") ;
arrayList.add("赵六") ;
arrayList.add("田七") ;
// arrayList = [张三, 李四, 王五, 赵六, 田七]
System.out.println("arrayList = " + arrayList);
// 调用Collections工具类中的方法操作ArrayList集合
// 集合反转
Collections.reverse(arrayList);
// arrayList = [田七, 赵六, 王五, 李四, 张三]
System.out.println("arrayList = " + arrayList);
// 对ArrayList集合元素进行随机排序
Collections.shuffle(arrayList);
// arrayList = [赵六, 田七, 李四, 王五, 张三]
System.out.println("arrayList = " + arrayList);
// 对ArrayList集合中的元素进行排序(默认排序规则)
Collections.sort(arrayList);
// arrayList = [张三, 李四, 王五, 田七, 赵六]
System.out.println("arrayList = " + arrayList);
// 对ArrayList集合中的元素进行排序(自定义排序规则排序规则)
Collections.sort(arrayList, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
// arrayList = [赵六, 田七, 王五, 李四, 张三]
System.out.println("arrayList = " + arrayList);
// 交换指定位置的元素
Collections.swap(arrayList , 0 , arrayList.size() - 1);
// arrayList = [张三, 田七, 王五, 李四, 赵六]
System.out.println("arrayList = " + arrayList);
}
}
Collections常用方法
- 查找、替换
- Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
- Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回 给定集合中的最大元素
- Object min(Collection) Object min(Collection,Comparator)
- int frequency(Collection,Object):返回指定集合中指定元素的出现次数
- void copy(List dest,List src):将src中的内容复制到dest中
- boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
- Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集 合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题