集合Collection:Java集合是一组用于存储和操作数据的类和接口(集合是一种可以存放多个数据的容器) Java集合主要接口和实现类 ▶ List(列表):提供有序、可重复的元素集合 ⯈ ArrayList(动态数组):基于动态数组的数据结构 —— 插入和删除效率低,元素访问效率高(非线程安全) 特性:① 有序 元素按照插入的顺序进行存储,通过索引访问和修改元素 ② 可重复 ArrayList允许存储重复的元素 ③ 动态扩容 元素数量超过当前容量时,会自动进行扩容以容纳更多的元素 ④ 随机访问 ArrayList内部使用数组实现,可以通过索引快速访问元素,时间复杂度为O(1) ⑤ 插入和删除效率较低 在ArrayList的中间位置插入或删除元素会导致后续元素的移动,时间复杂度为O(n) ⯈ LinkedList(链表):一个双向链表实现类,实现了List和Deque接口 —— 插入和删除效率高,元素访问效率低(非线程安全) 特性:① 有序 元素按照插入的顺序进行存储,通过索引访问和修改元素 ② 可重复 LinkedList允许存储重复的元素 ③ 链表结构 LinkedList内部使用链表数据结构来存储元素,每个元素都包含一个指向前一个元素和后一个元素的引用 ④ 动态添加和删除 由于LinkedList使用链表实现,插入和删除元素的效率较高,时间复杂度为O(1) ⑤ 随机访问效率较低 LinkedList不支持通过索引直接访问元素,效率较低,需要遍历链表来查找指定位置的元素,时间复杂度为O(n) ▶ Set(集):提供无序、不可重复的元素集合 ⯈ HashSet(基于哈希表): —— 插入和删除效率高,元素访问效率低(非线程安全) 特性:① 哈希表 HashSet内部使用哈希表数据结构来存储元素,通过计算元素的哈希值来确定其在哈希表中的位置 ② 无序性 HashSet中的元素是无序的,即元素之间没有固定的顺序 ③ 唯一性 HashSet中不能包含重复的元素,确保了集合中元素的唯一性 ④ 高效性 HashSet使用哈希表来存储元素,所以对于插入、删除和查找操作,平均时间复杂度为O(1) ⑤ 不支持索引 HashSet中的元素是无序的,所以无法通过索引来直接访问元素 ⯈ TreeSet(基于红黑树):一个集合类,实现了SortedSet接口并继承了AbstractSet类 特性:① 红黑树 TreeSet内部使用红黑树(Red-Black Tree)数据结构来存储元素,所以它具有自动排序的特性 ② 有序性 TreeSet中的元素按照排序顺序进行存储,默认是按照元素的自然顺序进行排序,或者可以通过传递一个自定义的比较器(Comparator)来指定排序规则 ③ 唯一性 TreeSet中不能包含重复的元素,确保了集合中元素的唯一性 ④ 高效性 TreeSet使用红黑树来存储元素,所以对于插入、删除和查找操作,平均时间复杂度为O(log n) ⑤ 支持导航方法 TreeSet提供了一些导航方法,如first()、last()、lower()、higher()等,可以方便地获取集合中的最小元素、最大元素,以及比给定元素小或大的元素 ▶ Queue(队列):提供一种先进先出(FIFO)的数据结构 ⯈ ArrayDeque(动态数组,包含LinkedList):都实现了Deque接口,可以用于实现队列(Queue)和双端队列(Deque)的操作 特性:① 自动扩容 ArrayDeque使用循环数组来存储元素,当数组元素不足时会自动扩容 ② 高效性 ArrayDeque对插入和删除操作具有较好的性能,时间复杂度为O(1),但在需要自动扩容时,插入和删除操作会带来较高的开销 ③ 随机访问 ArrayDeque支持通过索引直接访问元素,时间复杂度为O(1),但并不适合大量随机访问操作 Map(映射):提供一种键值对的映射关系 ⯈ HashMap(基于哈希表):一个集合类,实现了Map接口,用于存储键值对数据 特性:① 键值对存 HashMap以键值对的形式存储数据,每个键与唯一的值相关联 ② 高效性能 HashMap提供了常数时间复杂度(O(1))的插入、删除和查找操作,对于大多数情况下的操作都能够保持高效 ③ 无序性 HashMap中的元素没有固定的顺序,不会以插入的顺序或者键的自然顺序进行排序 ④ 允许null键和null值 HashMap允许键和值都为null,但只能有一个null键 ⑤ 键的唯一性 HashMap要求键的唯一性,即同一个HashMap对象中不能同时存在相同的键 ⯈ TreeMap(基于红黑树):一个集合类,实现了SortedMap接口 特性:① 排序 TreeMap中的键值对按照键的顺序进行排序(如果键是基本类型或String类型,则按照自然顺序排序。如果键是自定义类型,则需要实现Comparable接口或提供Comparator来定义比较规则) ② 唯一键 TreeMap中的键是唯一的,不允许重复(如果插入具有相同键的元素,则新元素将替换旧元素) ③ 搜索和检索 TreeMap支持高效的搜索和检索操作(如果键是基本类型或String类型,则按照自然顺序排序。如果键是自定义类型,则需要实现Comparable接口或提供Comparator来定义比较规则) ④ 遍历 TreeMap提供了多种遍历方式,包括按键升序、降序等方式遍历(通过keySet()方法获取键的集合,然后进行遍历。也可以使用entrySet()方法获取键值对的集合,然后进行遍历) ⑤ 子映射 TreeMap支持子映射操作,可以根据键的范围获取子映射 |
注意点:
HashSet
① 添加元素时,HashSet会根据元素的hashCode()方法返回的哈希值来确定元素在哈希表中的存储位置
② 为了保证元素的唯一性,HashSet会调用元素的equals()方法来判断两个元素是否相等
③ 在使用自定义对象作为HashSet的元素时,需要正确重写hashCode()和equals()方法,以确保元素的唯一性
TreeSet
① 添加元素时,TreeSet会根据元素的比较结果来确定元素在红黑树中的存储位置
② 为了保证元素的唯一性和排序准确性,TreeSet要求元素必须实现Comparable接口或者在创建TreeSet时传递自定义的比较器
③ 在使用自定义对象作为TreeSet的元素时,需要正确实现Comparable接口的compareTo()方法或者构造自定义的Comparator来定义排序规则
- 添加元素
➤boolean add(E element)
: 将指定元素添加到集合中,并返回添加是否成功
➤boolean addAll(Collection extends E> collection)
: 将指定集合中的所有元素添加到当前集合中,并返回添加是否成功- 删除元素
➤boolean remove(Object element)
: 从集合中删除指定元素,并返回删除成功与否
➤boolean removeAll(Collection> collection)
: 从集合中删除包含在指定集合中的所有元素,并返回删除成功与否
➤void clear()
: 清空集合中的所有元素- 判断集合是否包含元素
➤boolean contains(Object element)
: 判断集合是否包含指定元素
➤boolean containsAll(Collection> collection)
: 判断集合是否包含指定集合中的所有元素- 获取集合的大小和判断是否为空
➤int size()
: 返回集合中元素的数量
➤boolean isEmpty()
: 判断集合是否为空- 遍历集合:
➤Iterator
: 返回一个迭代器用于遍历集合中的元素。iterator()
➤forEach(Consumer super E> action)
: 对集合中的每个元素执行指定的操作。- 转换成数组
➤Object[] toArray()
: 将集合转换为一个数组
➤T[] toArray(T[] array)
: 将集合转换为指定类型的数组- 比较和排序
➤boolean equals(Object object)
: 判断集合是否与指定对象相等
➤int hashCode()
: 返回集合的哈希码值
➤void sort(Comparator super E> comparator)
: 对集合中的元素进行排序
package collections.list;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
/**
* 利用接口创建集合
*/
public class ArrayListDemo {
public static void main(String[] args) {
Collection arrayList = new ArrayList();
// add添加元素,返回布尔值
arrayList.add("集合");
arrayList.add("List");
arrayList.add("List");
arrayList.add("set");
System.out.println("集合ArrayList(有序):" + arrayList);
System.out.println("集合元素个数:" + arrayList.size());
System.out.println("集合是否为空:" + arrayList.isEmpty());
// 清空集合元素
arrayList.clear();
System.out.println("清空后的集合:" + arrayList);
// set
Collection set = new HashSet();
set.add("集合");
set.add("List");
set.add("List");
set.add("set");
System.out.println("集合set(无序,无重复):" + set);
// 利用自定义类测试
Collection points = new ArrayList();
points.add(new Point(1, 2));
points.add(new Point(1, 2));
points.add(new Point(1, 8));
points.add(new Point(5, 8));
System.out.println("集合的元素:" + points);
// contains方法(底层使用的是equals方法,需要在Point类中重写equals方法)
boolean contains = points.contains(new Point(1, 2));
System.out.println("是否包含元素:" + contains);
// remove删除(只删除匹配到的第一个元素)
Point a = new Point(1, 2);
points.remove(a);
System.out.println("删除后的集合:" + points);
//
Collection hashSet = new HashSet();
Collection system = new HashSet();
hashSet.add("java");
hashSet.add("python");
hashSet.add("C++");
hashSet.add("android");
hashSet.add("ios");
System.out.println("hashset集合:" + hashSet);
// system集合
system.add("android");
system.add("ios");
System.out.println("system集合:" + system);
boolean containsAll = hashSet.containsAll(system);
System.out.println("hashset集合是否全部包含system集合:" + containsAll);
// 取交集retainAll(只影响hashset)
hashSet.retainAll(system);
System.out.println("(交集)hashset集合:" + hashSet);
System.out.println("(交集)system集合:" + system);
// 集合存放引用数据类型
Point p = new Point(1, 2);
Collection ps = new ArrayList();
ps.add(p);
System.out.println("修改之前:" + ps);
p.setX(5);
p.setY(6);
System.out.println("修改之后:" + ps);
}
}
package collections.list;
import java.util.Objects;
public class Point {
private int x;
private int y;
public Point() {
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return "(" + x + ", " + y + ")";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
泛型:Java中编写类型安全且可重用代码的一种机制 作用:在编写代码时使用指定类型,而不是在运行时强制转换类型 ▶ 泛型是Java SE5.0引入的特性,泛型的本质是参数化类型 ▶ 类、接口和方法的定义过程中,所操作的数据类型由被传入的参数所指定 ▶ 往集合中存放基本数据类型时,(继承于object)会触发自动装箱操作 ▶ Java泛型机制广泛的应用在集合框架中,所有的集合类型都带有泛型参数(创建集合时可以指定放入集合中元素的类型) |
▶ 注:
集合中存储的都是引用类型元素,且值保存每个元素对象的引用,而不是将元素对象本身存入集合
往集合中存放基本数据类型时,(继承于object)会触发自动装箱操作
当用一个输入的字符串和已知字符串做比较时,通常使用已知字符.equals(输入字符)
—— 输入空字符时不会报错,程序继续执行;输入字符.equals(已知字符)
—— 输入空字符而使程序报错
增强for循环
for(数据类型 变量名: 集合/数组){ 操作 }
package collections.list;
import java.util.*;
/**
* 泛型: JDK5之后提出的新特性
*/
public class Genericity {
public static void main(String[] args) {
// 泛型初步使用
Collection<String> list = new ArrayList<>();
list.add("one");
list.add("#");
list.add("tow");
list.add("#");
list.add("three");
list.add("#");
list.add("four");
list.add("#");
list.add("five");
System.out.println("String集合:" + list);
/*
* 集合的遍历 集合提供了统一的遍历方式:迭代器模式
* Iterator iterator(),使用时也要指定泛型
*/
Iterator<String> it = list.iterator();
while (it.hasNext()){
String elment = it.next();
if ("#".equals(elment)){
// 删除#
it.remove();
}
}
System.out.println("删除之后的集合:" + list);
/*
* 存入整数,遍历
*/
Collection<Integer> integer = new ArrayList<>();
for (int i = 1; i <= 100; i++) {
integer.add(i);
}
System.out.println("整型集合:" + integer);
Iterator<Integer> iterator = integer.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
/*
* 增强for循环
*/
for (String e : list) {
System.out.println("增强for循环:" + e);
}
LinkedList linkedList = new LinkedList<>();
linkedList.add("java");
linkedList.add("python");
linkedList.add("c++");
System.out.println("获取第三个元素:" + linkedList.get(2));
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
// String old = String.valueOf(linkedList.set(2, "C#"));
var old = linkedList.set(2, "C#");
System.out.println("替换后的集合:" + linkedList);
System.out.println("被替换的元素:" + old);
var olds = linkedList.remove(2);
System.out.println("删除后的集合:" + linkedList);
System.out.println("被删除的元素:" + olds);
// 集合的截取
List<Integer> list1 = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list1.add(i);
}
List<Integer> subLists = list1.subList(3, 8);
System.out.println("子集:" + subLists);
for (int i = 0; i < list1.size(); i++) {
list1.set(i, list1.get(i) * 10);
}
System.out.println("元素×10之后的集合:" + list1);
// 删除(利用子集:对子集的操作就是对原集合的操作)/clear()
list1.removeAll(list1.subList(3, 8));
System.out.println("删除后的集合:" + list1);
}
}
List接口:Collection的子接口,用于定义线性表数据结构(相当于存放对象的数组,元素个数可以动态增减) List的实现类:ArrayList(动态数组)和LinkedList(链表),二者逻辑一样,性能不同 |
数组与集合之间的转换
集合转数组:集合名.toArray(new 数组类型[数组长度]);
package connection.list;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CollectionToArray {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
strings.add("one");
strings.add("tow");
strings.add("three");
strings.add("four");
strings.add("five");
// 集合转为数组
String[] array = strings.toArray(new String[strings.size()]);
System.out.println("数组:" + Arrays.toString(array));
}
}
数组转集合:Arrays.asList(数组);
直接由数组转化而来的集合,因为数组定长的特性(长度不变),对应的也无法给集合添加新元素;需要修改时,需要重新创建集合(将由数组转化的集合作为新创建数组的参数)
即返回的集合我们不能对其增删元素,否则会抛出异常
对集合的元素进行修改会影响数组对应的元素
package connection.list;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 数组转为List
* @author LongYongchuan
*/
public class AsList {
public static void main(String[] args) {
String[] strings = {"one", "tow", "three", "four", "five"};
System.out.println("数组:" + Arrays.toString(strings));
// 转化
List<String> list = Arrays.asList(strings);
System.out.println("List集合:" + list);
// 对集合元素的操作就是对数组的操作
list.set(1, "二");
System.out.println("修改元素后的集合:" + list);
System.out.println("也作用于数组:" + Arrays.toString(strings));
// 根据数组定长的特性(长度不变),对应的也无法给集合添加新元素
// list.add("5"); // ----- 不支持操作异常
// 需要修改时,需要重新创建集合(将由数组转化的集合作为新创建数组的参数)
List<String> lists = new ArrayList<>(list);
System.out.println("新创建的集合:" + lists);
// 添加元素
lists.add("七");
System.out.println("添加之后的集合:" + lists);
}
}
Collections类的sort()方法:对List集合进行原地排序(即在原集合上进行排序) Stream API的sorted()方法:sorted()方法返回一个新的排序后的流,不会修改原始集合 ▶ Collections.sort()方法可以直接对List集合进行排序 ▶ Collections.sort()方法结合自定义的Comparator(匿名内部类)来对对象进行排序 ⯈ 字符串的排序是按照Unicode编码进行排序的(对中文的排序无意义) |
package connection.list;
import java.util.*;
/**
* 对集合进行排序
* @author LongYongchuan
*/
public class SortList {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
list.add(random.nextInt(100));
}
System.out.println("原集合:" + list);
// 对集合进行排序
Collections.sort(list);
System.out.println("排序后的集合:" + list);
}
}
package connection.list;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class StringSort {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Tom");
list.add("tab");
list.add("jack");
list.add("Bob");
list.add("rose");
list.add("ada");
list.add("Alen");
list.add("bill");
System.out.println("集合:" + list);
// 排序(按照ASCII码排序,对中文排序没有意义)
Collections.sort(list);
System.out.println("排序后的集合:" + list);
}
}
lambda表达式:Java 8引入的一种函数式编程的特性,它提供了一种简洁、灵活的方式来表示匿名函数 ▶ 格式:(parameter_list) -> { lambda_body } ⯈ 参数列表(parameter_list):指定了Lambda表达式所使用的参数,可以为空或包含一个或多个参数 ⯈ 箭头符号(->):将参数列表与Lambda体分隔开 ⯈ Lambda体(lambda_body):指定了Lambda表达式的执行逻辑,可以是一个表达式或代码块 形式:① 无参数的Lambda表达式 () -> { System.out.println("Hello, World!"); } ② 单个参数的Lambda表达式 (x) -> { System.out.println(x); } 简写: x -> System.out.println(x); ③ 多个参数的Lambda表达式 (x, y) -> { System.out.println(x + y); } |
当要实现的接口有且只有一个抽象方法时,可以利用lambda表达式简写
参数类型可以不写,方法体可以不写(方法体只有一句代码),同时必须忽略return关键字
通常格式: (参数列表) ->{
方法体
}
方法引用
静态方法引用:ClassName::staticMethodName
例:Math::max(引用Math类中的静态方法max)
实例方法引用:objectReference::instanceMethodName
例:String::length(引用字符串对象的length方法)
对象方法引用:ClassName::instanceMethodName
例:ArrayList::size(引用ArrayList类实例的size方法)
构造方法引用:ClassName::new
例:ArrayList::new(引用ArrayList类的构造方法)
方法引用可以简化代码,并提高可读性;通常与函数式接口(Functional Interface)一起使用,用于传递方法或构造方法作为参数
package lambda;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Lambda表达式
* lambda表达式可以用更简洁的语法创建匿名内部类lambda表达式又称为“箭头函数",
* 主要目的是省去了匿名内部类创建时接口与方法的编写,可以更突出重写方法的逻辑部分。
* 只有当实现的接口中包含唯一一个抽象方法时才可以使用Lambda表达式
* 语法:
* (参数列表)->{
* 方法体
* }
* @author LongYongchuan
*/
public class LambdaDemo {
public static void main(String[] args) {
List<String> name = new ArrayList<>();
name.add("戚娜娜");
name.add("毛文");
name.add("郝楠楠");
name.add("欧阳云宇");
name.add("董瑞");
Collections.sort(name, comparators);
System.out.println("按照名字长度排序:" + name);
}
// lambda表达式
static Comparator<String> comparator = (String o1, String o2) -> {
return o1.length() - o2.length();
};
// lambda表达式中的方法参数类型可以不写(方法体只有一行代码时,return及{}可以不写)
static Comparator<String> comparator1 = (o1,o2) -> o1.length() - o2.length();
// 精简写法
static Comparator<String> comparators = Comparator.comparingInt(String::length);
}
package lambda;
import java.util.ArrayList;
import java.util.Collection;
public class LambdaTraverse {
public static void main(String[] args) {
Collection<String> num = new ArrayList<>();
num.add("one");
num.add("tow");
num.add("three");
num.add("four");
num.add("five");
System.out.println("集合:" + num);
// lambda表达式
num.forEach(o -> System.out.println(o));
// 精简(方法引用)
num.forEach(System.out::println);
}
}