资料
- 参考博文:https://blog.csdn.net/weixin_44176393/article/details/110367870
- 参考:https://www.runoob.com/java/java-collections.html
- 参考:https://www.liaoxuefeng.com/wiki/1252599548343744/1265112034799552
学习目标
- 了解集合类的基本架构
- 了解ArrayList、LinkedList的用法、优缺点
- 了解Set的用法、优缺点
- 了解Map、TreeMap、HashMap的用法、优缺点
- 了解常用数据结构
基础知识
集合就是“由若干个确定的元素所构成的整体”,对象的容器,定义了对多个对象进行操作的常用方法,可实现数组的功能。
集合框架图:  削减版的架构图:  集合和数组的区别:
- 数组长度固定,集合长度不固定
- Java集合如Map、Set、List等所有集合
只能存放引用类型数据 ,它们都是存放引用类型数据的容器,不能存放如int、long、float、double等基础类型的数据。 可以通过包装类把基本类型转为对象类型,存放引用就可以解决这个问题。
Collection
Java标准库自带的java.util 包提供了集合类:Collection ,它是除Map 外所有其他集合类的根接口 。Java的java.util 包主要提供了以下三种类型的集合:
- List:
一种有序列表的集合,允许重复元素 - Set:
一种保证没有重复元素的集合,无序集合 - Map:
一种通过键值(key-value)查找的映射表集合
集合常用方法:
1、boolean add(Object obj):添加一个对象
2、boolean addAll(Collection c):将一个集合中的所有对象添加到此集合中
3、void clear():清空此集合中的所有对象
4、boolean contains(Object o):检查此集合中是否包含o对象
5、boolean equals(Object o):比较此集合是否与指定对象相等
6、boolean isEmpty():判断此集合是否为空
7、boolean remove(Object o):在此集合中移除o对象
8、int size():返回此集合中的元素个数
9、Object[ ] toArray():将此集合转换成数组
10、Iterator< > iterator():返回在此collection的元素上进行迭代的迭代器
示例代码:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo01 {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("苹果");
collection.add("香蕉");
collection.add("梨");
System.out.println("元素个数:"+collection.size());
System.out.println(collection);
collection.remove("香蕉");
System.out.println("删除之后元素个数:"+collection.size());
for (Object object: collection
) {
System.out.println(object);
}
System.out.println("-----------------------------");
Iterator it = collection.iterator();
while(it.hasNext()){
String s = (String)it.next();
System.out.println(s);
it.remove();
}
System.out.println("当前元素:"+collection.size());
System.out.println("----------------");
System.out.println(collection.contains("西瓜"));
System.out.println(collection.isEmpty());
}
}
package study.gather.Demo01;
import java.util.ArrayList;
import java.util.Collection;
public class Demo02 {
public static void main(String[] args) {
Collection collection = new ArrayList();
Student s1 = new Student("张三", 20);
Student s2 = new Student("李四", 25);
Student s3 = new Student("王五", 24);
collection.add(s1);
collection.add(s2);
collection.add(s3);
System.out.println("元素个数:"+collection.size());
System.out.println(collection.toString());
collection.remove(s1);
System.out.println("元素个数:"+collection.size());
collection.clear();
}
}
重写equals方法
如何正确编写equals()方法?equals()方法要求我们必须满足以下条件:
- 自反性(Reflexive):对于非null的x来说,x.equals(x)必须返回true;
- 对称性(Symmetric):对于非null的x和y来说,如果x.equals(y)为true,则y.equals(x)也必须为true;
- 传递性(Transitive):对于非null的x、y和z来说,如果x.equals(y)为true,y.equals(z)也为true,那么x.equals(z)也必须为true;
- 一致性(Consistent):对于非null的x和y来说,只要x和y状态不变,则x.equals(y)总是一致地返回true或者false;
- 对null的比较:即x.equals(null)永远返回false。
我们总结一下equals()方法的正确编写方法:
- 先确定实例“相等”的逻辑,即哪些字段相等,就认为实例相等;
- 用
instanceof 判断传入的待比较的Object 是不是当前类型,如果是,继续比较,否则,返回false ; - 对引用类型用
Objects.equals() 比较,对基本类型直接用==比较。 - 使用
Objects.equals() 比较两个引用类型是否相等的目的是省去了判断null 的麻烦。两个引用类型都是null 时它们也是相等的。 - 如果不调用
List 的contains() 、indexOf() 这些方法,那么放入的元素就不需要实现equals() 方法。
示例代码:
import java.util.List;
import java.util.Objects;
public class HelloWord {
public static void main(String[] args) {
List<Person> list = List.of(
new Person("Xiao", "Ming", 18),
new Person("Xiao", "Hong", 25),
new Person("Bob", "Smith", 20)
);
boolean exist = list.contains(new Person("Bob", "Smith", 20));
System.out.println(exist ? "测试成功!" : "测试失败!");
}
}
class Person {
String firstName;
String lastName;
int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
@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(firstName, person.firstName) && Objects.equals(lastName, person.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, lastName, age);
}
}
List
在集合类中,List 是最基础的一种集合:它是一种有序列表 。 List 的行为和数组几乎完全相同:List内部按照放入元素的先后顺序存放,每个元素都可以通过索引确定自己的位置,List 的索引和数组一样,从0 开始。
ArrayList
 引用包:
import java.util.ArrayList;
创建方法:
ArrayList<T> arrayList = new ArrayList<T>();
示例代码:
import java.util.ArrayList;
import java.util.Comparator;
public class RunoobTest {
public static void main(String[] args) {
ArrayList<String> sites = new ArrayList<String>();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Weibo");
System.out.println(sites);
System.out.println(sites.get(1));
sites.set(2, "Wiki");
System.out.println(sites);
sites.remove(3);
System.out.println(sites);
System.out.println(sites.size());
for (int i = 0; i < sites.size(); i++) {
System.out.println(sites.get(i));
}
for (String i : sites) {
System.out.println(i);
}
sites.sort(Comparator.naturalOrder());
System.out.println("排序后: " + sites);
}
}
List转Array:
import java.util.List;
public class HelloWord {
public static void main(String[] args) {
List<String> list = List.of("apple", "pear", "banana");
Object[] array = list.toArray();
for (Object s : array) {
System.out.println(s);
}
String[] stringArray1 = list.toArray(new String[3]);
for (String n : stringArray1) {
System.out.println(n);
}
String[] stringArray2= list.toArray(new String[list.size()]);
for (String n : stringArray2) {
System.out.println(n);
}
String[] stringArray3 = list.toArray(String[]::new);
for (String n : stringArray3) {
System.out.println(n);
}
}
}
Array变List
import java.util.List;
public class HelloWord {
public static void main(String[] args) {
List<Integer> list = List.of(12, 34, 56);
System.out.println(list);
list.add(999);
}
}
常用API:
方法 | 描述 |
---|
add() | 将元素插入到指定位置的 arraylist 中 | addAll() | 添加集合中的所有元素到 arraylist 中 | clear() | 删除 arraylist 中的所有元素 | clone() | 复制一份 arraylist | contains() | 判断元素是否在 arraylist | get() | 通过索引值获取 arraylist 中的元素 | indexOf() | 返回 arraylist 中元素的索引值 | removeAll() | 删除存在于指定集合中的 arraylist 里的所有元素 | remove() | 删除 arraylist 里的单个元素 | size() | 返回 arraylist 里元素数量 | isEmpty() | 判断 arraylist 是否为空 | subList() | 截取部分 arraylist 的元素 | set() | 替换 arraylist 中指定索引的元素 | sort() | 对 arraylist 元素进行排序 | toArray() | 将 arraylist 转换为数组 | toString() | 将 arraylist 转换为字符串 | ensureCapacity() | 设置指定容量大小的 arraylist | lastIndexOf() | 返回指定元素在 arraylist 中最后一次出现的位置 | retainAll() | 保留 arraylist 中在指定集合中也存在的那些元素 | containsAll() | 查看 arraylist 是否包含指定集合中的所有元素 | trimToSize() | 将 arraylist 中的容量调整为数组中的元素个数 | removeRange() | 删除 arraylist 中指定索引之间存在的元素 | replaceAll() | 将给定的操作内容替换掉数组中每一个元素 | removeIf() | 删除所有满足特定条件的 arraylist 元素 | forEach() | 遍历 arraylist 中每一个元素并执行特定操作 |
小结:
- ArrayList必须要连续空间,查询快、增删慢
- 频繁访问列表中的某一个元素时使用ArrayList。
- 只需要在列表末尾进行添加和删除元素操作时使用ArrayList。
LinkedList
链表(Linked list )是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。链表可分为单向链表和双向链表。
一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接。  一个双向链表有三个整数值: 数值、向后的节点链接、向前的节点链接。  以下情况使用 LinkedList :
- 你需要通过循环迭代来访问列表中的某些元素。
- 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
LinkedList 继承了 AbstractSequentialList 类。 LinkedList 实现了 Queue 接口,可作为队列使用。 LinkedList 实现了 List 接口,可进行列表的相关操作。 LinkedList 实现了 Deque 接口,可作为队列使用。 LinkedList 实现了 Cloneable 接口,可实现克隆。 LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输  引用包:import java.util.LinkedList; 创建方法:
LinkedList<E> list = new LinkedList<E>();
或者
LinkedList<E> list = new LinkedList(Collection<? extends E> c);
示例代码:
import java.util.LinkedList;
public class HelloWord {
public static void main(String[] args) {
LinkedList<String> sites = new LinkedList<String>();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Weibo");
sites.addFirst("Baidu");
sites.addLast("Alibaba");
for (int size = sites.size(), i = 0; i < size; i++) {
System.out.println(sites.get(i));
}
sites.removeFirst();
sites.removeLast();
for (String i : sites) {
System.out.println(i);
}
System.out.println(sites.getFirst());
System.out.println(sites.getLast());
}
}
常用方法:传送门 小结:
- API
- 首尾添加
- 首尾获取
- 首尾移除
- (双向)链表结构:
- 每一个元素都分为上一个,下一个,当前元素
- 每次增加元素,只新增一个对象,极大提高了集合元素变化的性能
- 对元素的访问性能不高,因为其本质是没有游标的,要访问某个元素都是从首尾开始迭代
Set
如果我们只需要存储不重复的key ,并不需要存储映射的value,那么就可以使用Set 。 特点:
- 无序、无下标、元素不可重复
- Set实现了Collection接口中定义的方法。
Set用于存储不重复 (唯一)的元素集合,它主要提供以下几个方法:
- 将元素添加进Set:boolean
add (E e) - 将元素从Set删除:boolean
remove (Object e) - 判断是否包含元素:boolean
contains (Object e)
Set实际上相当于只存储key、不存储value的Map。我们经常用Set用于去除重复元素。  Set接口并不保证有序,而SortedSet接口则保证元素是有序的:
HashSet 是无序的,因为它实现了Set 接口,并没有实现SortedSet 接口;TreeSet 是有序的,因为它实现了SortedSet 接口。
HashSet
没有真正意义上的set集合,他使用的是HashMap 的key。 方法:
- 新建集合
HashSet<String> hashSet = new HashSet<String>(); - 添加元素
hashSet.add( ); - 删除元素
hashSet.remove( ); - 遍历操作
增强for for(type type : hashSet) 迭代器 Iterator<String> it = hashSet.iterator( ); - 判断
hashSet.contains( ); , hashSet.isEmpty();
TreeSet
特点:
- 基于排列顺序实现元素不重复
- 实现
SortedSet 接口,对集合元素自动排序 - 元素对象的类型必须实现
Comparable 接口,指定排序规则 - 通过
CompareTo 方法确定是否为重复元素 - 使用TreeSet和使用TreeMap的要求一样,添加的元素必须正确实现
Comparable 接口,如果没有实现Comparable 接口,那么创建TreeSet时必须传入一个Comparator 对象。
方法:
- 创建集合 TreeSet treeSet = new TreeSet<>()
- 添加元素 treeSet.add();
- 删除元素 treeSet.remove();
- 遍历 1. 增强for 2. 迭代器
- 判断 treeSet.contains();
使用:要求:元素必须实现Comparable 接口,compareTo() 方法的返回值为0,认为是重复元素
@override
public int compareTo(Person o){
int n1 = this.getName().compareTo(o.getName());
int n2 = this.getAge()-o.getAge();
return n1 == 0 ? n2 : n1;
}
或者 传入一个Comparator 对象
TreeSet<Person> treeSet = new TreeSet<>(new Comparator<Person>){
@override
public int compare(Person o1,Person o2){
int n1 = o1.getName().compareTo(o2.getName());
int n2 = o1.getAge()-o2.getAge();
return n1 == 0 ? n2 : n1;
}
}
Map
特点:存储一对数据(Key-Value)【键值对】,无序、无下标、键不可重复,值可重复
HashMap
 方法:
- V
put (K key,V value):将对象存入到集合中,关联键值。key重复则覆盖原值 - Object
get (Object key):根据键获取对应的值 keySet ():返回所有keyvalues ():返回包含所有值的Collection集合- Set<Map.Entry<K,V>>
entrySet() :键值匹配的Set集合 - boolean
containsKey (K key):判断key是否存在
示例代码:
import java.util.HashMap;
import java.util.Map;
public class HelloWord {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("cn", "中国");
map.put("uk", "英国");
map.put("cn", "zhongguo");
map.remove("uk");
for(String key : map.keySet()){
System.out.println(key + "---" + map.get(key));
}
for(Map.Entry<String, String> entry : map.entrySet()){
System.out.println(entry.getKey() + "---" + entry.getValue());
}
System.out.println(map.containsKey("cn"));
System.out.println(map.containsValue("泰国"));
}
}
备注 :Java这块的Map和C#中的Dictionary不太一样,如果key已经存在了再添加同名的key不会报错。
重复放入key-value并不会有任何问题,但是一个key只能关联一个value。在上面的代码中,一开始我们把key对象"apple"映射到Integer对象123,然后再次调用put()方法把"apple"映射到789,这时,原来关联的value对象123就被“冲掉”了。实际上,put()方法的签名是V put(K key, V value),如果放入的key已经存在,put()方法会返回被删除的旧的value,否则,返回null。
要正确使用HashMap ,作为key的类必须正确覆写equals() 和hashCode() 方法; 一个类如果覆写了equals(),就必须覆写hashCode(),并且覆写规则是:
- equals()返回true,则hashCode()返回值必须相等;
- 如果equals()返回false,则hashCode()返回值尽量不要相等。
- 实现hashCode()方法可以通过Objects.hashCode()辅助方法实现。
其他数据结构
EnumMap
如果作为key的对象是enum类型,那么,还可以使用Java集合库提供的一种EnumMap,它在内部以一个非常紧凑的数组存储value,并且根据enum类型的key直接定位到内部数组的索引,并不需要计算hashCode(),不但效率最高,而且没有额外的空间浪费。
示例代码:
import java.time.DayOfWeek;
import java.util.EnumMap;
import java.util.Map;
public class AAA {
public static void main(String[] args) {
Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);
map.put(DayOfWeek.MONDAY, "星期一");
map.put(DayOfWeek.TUESDAY, "星期二");
map.put(DayOfWeek.WEDNESDAY, "星期三");
map.put(DayOfWeek.THURSDAY, "星期四");
map.put(DayOfWeek.FRIDAY, "星期五");
map.put(DayOfWeek.SATURDAY, "星期六");
map.put(DayOfWeek.SUNDAY, "星期日");
System.out.println(map);
System.out.println(map.get(DayOfWeek.MONDAY));
}
}
小结:
- 如果
Map 的key 是enum 类型,推荐使用EnumMap ,既保证速度,也不浪费空间。 - 使用
EnumMap 的时候,根据面向抽象编程的原则,应持有Map 接口。
TreeMap
SortedMap在遍历时严格按照Key的顺序遍历,最常用的实现类是TreeMap;
- 作为SortedMap的Key必须实现
Comparable 接口,或者传入Comparator ; - 要严格按照
compare() 规范实现比较逻辑,否则,TreeMap将不能正常工作。 示例代码:
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class AAA {
public static void main(String[] args) {
Map<Student, Integer> map = new TreeMap<>(new Comparator<Student>() {
public int compare(Student p1, Student p2) {
if (p1.score == p2.score) {
return 0;
}
return p1.score > p2.score ? -1 : 1;
}
});
map.put(new Student("Tom", 77), 1);
map.put(new Student("Bob", 66), 2);
map.put(new Student("Lily", 99), 3);
for (Student key : map.keySet()) {
System.out.println(key);
}
System.out.println(map.get(new Student("Bob", 66)));
}
}
class Student {
public String name;
public int score;
Student(String name, int score) {
this.name = name;
this.score = score;
}
public String toString() {
return String.format("{%s: score=%d}", name, score);
}
}
思考题
|