什么是封装
封装是将对象的属性和方法(或称为成员)结合成一个独立的单元,隐藏对象的属性和实现细节,仅对外公开接口(方法)与对象进行交互。
链表数据结构
链表是一种常见的数据结构,它由一系列节点(Node)组成,每个节点包含数据和一个或多个指向其他节点的引用(即指针)。与数组不同,链表中的元素在内存中不是连续存储的。链表提供了一种灵活的方式来组织数据,尤其适用于需要频繁插入和删除操作的场景。
链表的类型
-
单向链表(Singly Linked List):
- 每个节点包含数据和一个指向下一个节点的引用。
- 最后一个节点的引用指向
null
,表示链表的结束。
-
双向链表(Doubly Linked List):
- 每个节点包含数据、一个指向下一个节点的引用和一个指向前一个节点的引用。
- 链表的头节点的前引用和尾节点的后引用指向
null
。
-
循环链表(Circular Linked List):
- 单向或双向链表的一种变体,其中最后一个节点的引用指向头节点,形成一个环。
-
双向循环链表(Doubly Circular Linked List):
- 双向链表的一种变体,最后一个节点的后引用指向头节点,头节点的前引用指向尾节点。
单向链表的实现示例
以下是一个简单的单向链表的实现:
class Node {
int data;
Node next;
Node(int data) {
this.data = data;
this.next = null;
}
}
class SinglyLinkedList {
Node head;
// 添加元素到链表的头部
public void addFirst(int data) {
Node newNode = new Node(data);
newNode.next = head;
head = newNode;
}
// 添加元素到链表的尾部
public void addLast(int data) {
Node newNode = new Node(data);
if (head == null) {
head = newNode;
return;
}
Node current = head;
while (current.next != null) {
current = current.next;
}
current.next = newNode;
}
// 打印链表中的所有元素
public void printList() {
Node current = head;
while (current != null) {
System.out.print(current.data + " ");
current = current.next;
}
System.out.println();
}
}
public class Main {
public static void main(String[] args) {
SinglyLinkedList list = new SinglyLinkedList();
list.addFirst(1);
list.addLast(2);
list.addLast(3);
list.printList(); // 输出: 1 2 3
}
}
链表的优点
- 动态大小:链表可以动态调整大小,没有固定的容量限制。
- 插入和删除效率高:在已知位置插入或删除元素的时间复杂度为O(1)。
- 内存利用率高:链表的内存利用率比数组高,因为不需要预先分配大量连续内存。
链表的缺点
- 访问效率低:链表不支持高效的随机访问,访问任意元素的时间复杂度为O(n)。
- 额外的内存开销:每个节点除了存储数据外,还需要存储指向下一个节点的引用,增加了内存开销。
- 复杂的实现:与数组相比,链表的实现和操作更复杂,需要处理指针的维护。
链表的应用场景
- 频繁插入和删除操作:例如实现队列、栈等数据结构。
- 需要动态调整大小的场景:例如实现链式散列表(Hash Table)。
- 内存利用率要求高的场景:例如大数据处理中的内存管理。
arrylist vector linklist 的区别
ArrayList
、Vector
和 LinkedList
是 Java 中常用的集合类,它们都有各自的特性和用途。以下是它们之间的主要区别:
1. ArrayList
- 底层实现:基于动态数组。
- 线程安全:不是线程安全的,多个线程同时访问时需要手动同步。
- 性能:由于是动态数组,支持快速随机访问(时间复杂度为 O(1)),但在插入和删除元素时,特别是在数组中间位置,会涉及大量元素的移动,性能较低(时间复杂度为 O(n))。
- 适用场景:适用于频繁读取和访问元素的场景。
2. Vector
- 底层实现:基于动态数组。
- 线程安全:线程安全的,所有方法都被 synchronized 修饰,多个线程可以安全地访问。
- 性能:由于线程安全的开销,性能比
ArrayList
略低。同样支持快速随机访问,但插入和删除元素时性能较低。 - 适用场景:适用于多线程环境下,需要频繁读取和访问元素的场景。
3. LinkedList
- 底层实现:基于双向链表。
- 线程安全:不是线程安全的,多个线程同时访问时需要手动同步。
- 性能:不支持高效的随机访问(时间复杂度为 O(n)),但在插入和删除元素时,性能较高(在链表两端操作的时间复杂度为 O(1),在中间插入删除的时间复杂度为 O(n))。
- 适用场景:适用于频繁插入和删除元素的场景。
具体区别总结
- 实现方式:
ArrayList
和Vector
基于动态数组实现,而LinkedList
基于双向链表实现。 - 线程安全:
Vector
是线程安全的,ArrayList
和LinkedList
不是线程安全的。 - 性能:
ArrayList
和Vector
提供了快速的随机访问,但插入和删除性能较低;LinkedList
插入和删除性能较高,但随机访问性能较低。 - 内存使用:
ArrayList
和Vector
的内存使用更加紧凑,LinkedList
由于每个节点需要存储指针,内存开销较大。
使用示例
import java.util.ArrayList;
import java.util.Vector;
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
// ArrayList 示例
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("A");
arrayList.add("B");
System.out.println("ArrayList: " + arrayList);
// Vector 示例
Vector<String> vector = new Vector<>();
vector.add("A");
vector.add("B");
System.out.println("Vector: " + vector);
// LinkedList 示例
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("A");
linkedList.add("B");
System.out.println("LinkedList: " + linkedList);
}
}
选择哪种集合类,取决于具体的使用场景和需求。
迭代器 简述
迭代器(Iterator)是Java集合框架中的一种对象,用于遍历集合(如ArrayList
、HashSet
等)中的元素。它提供了一种统一的方式来访问集合中的每一个元素,而不需要了解集合的底层实现。以下是迭代器的主要特性和使用方法:
主要特性
- 遍历集合:迭代器可以遍历集合中的每一个元素。
- 无须了解底层实现:使用迭代器时,不需要知道集合的具体实现方式,只需要按照统一的接口操作。
- 单向遍历:迭代器通常只能单向遍历集合,从第一个元素开始,一直到最后一个元素。
- 安全删除:通过迭代器,可以在遍历过程中安全地删除元素。
常用方法
迭代器接口 (java.util.Iterator
) 提供了以下三个主要方法:
boolean hasNext()
:如果迭代器中还有元素可以迭代,则返回true
。E next()
:返回迭代器中的下一个元素。void remove()
:从集合中删除迭代器返回的上一个元素(可选操作)。
使用示例
以下是使用迭代器遍历 ArrayList
的一个简单示例:
import java.util.ArrayList;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// 获取迭代器
Iterator<String> iterator = list.iterator();
// 使用迭代器遍历集合
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}
}
}
迭代器的优势
- 统一遍历方式:无论集合类型是什么,使用迭代器都可以统一地遍历集合。
- 避免并发修改异常:在遍历过程中,如果使用集合自身的修改方法(如
add
、remove
),可能会导致ConcurrentModificationException
。使用迭代器的remove
方法可以安全地删除元素。 - 简化代码:迭代器可以简化代码,使得遍历集合的代码更加清晰和简洁。
注意事项
- 只读迭代器:对于某些集合(如
Collections.unmodifiableList
返回的集合),迭代器可能是只读的,调用remove
方法会抛出UnsupportedOperationException
。 - 并发修改:如果在迭代过程中修改了集合(不是通过迭代器),可能会导致
ConcurrentModificationException
。要在遍历过程中安全地修改集合,应该使用迭代器的remove
方法。
jdk1.8新特性
JDK 1.8(也称为Java 8)引入了许多新特性和改进,使其成为Java平台的一个重要版本。以下是一些主要的新特性:
-
Lambda 表达式:
- Lambda 表达式提供了一种简洁的方式来传递行为,使代码更简洁和灵活。
- 语法:
(参数) -> 表达式
或(参数) -> { 代码块 }
- 例如:
(int x, int y) -> x + y
表示一个接收两个整数并返回它们和的函数。
-
函数式接口:
- 函数式接口是仅包含一个抽象方法的接口,用于Lambda表达式和方法引用。
- Java 8 提供了许多内置的函数式接口,如
Function<T, R>
,Consumer<T>
,Supplier<T>
,Predicate<T>
等。 - 可以用
@FunctionalInterface
注解来标识函数式接口。
-
方法引用:
- 方法引用提供了一种简洁的Lambda表达式的替代语法。
- 语法:
Class::methodName
或instance::methodName
- 例如:
String::valueOf
表示valueOf
静态方法的引用。
-
接口默认方法:
- 接口现在可以有默认方法,允许在不破坏现有实现的情况下添加新方法。
- 使用
default
关键字来定义默认方法。 - 例如:
default void log(String message) { System.out.println(message); }
-
Stream API:
- Stream API 用于处理集合类(如List、Set)的数据操作。
- 支持对集合进行声明性的数据处理,如过滤、排序、映射等。
- 例如:
List<String> filteredList = list.stream().filter(s -> s.startsWith("a")).collect(Collectors.toList());
-
Optional 类:
Optional
类用于防止出现空指针异常。- 提供了许多方法来安全地处理可能为空的值。
- 例如:
Optional<String> optional = Optional.ofNullable(value);
-
新的日期时间 API:
- 引入了
java.time
包,提供了更好的日期时间处理。 - 包括
LocalDate
,LocalTime
,LocalDateTime
,ZonedDateTime
等类。 - 例如:
LocalDate date = LocalDate.now();
- 引入了
-
Nashorn JavaScript 引擎:
- Java 8 引入了 Nashorn,一个新的 JavaScript 引擎,用于替代 Rhino。
- 允许在 Java 应用中嵌入 JavaScript 代码。
-
Base64 编码和解码:
- 提供了
java.util.Base64
类用于Base64编码和解码。 - 例如:
String encoded = Base64.getEncoder().encodeToString("hello".getBytes());
- 提供了
-
并行数组(Parallel Array):
- 提供了
Arrays.parallelSort
方法,用于并行排序数组,提高大数据集的处理性能。
- 提供了
-
改进的集合 API:
- 添加了一些新的方法,如
forEach
、removeIf
、replaceAll
、compute
、merge
等。
- 添加了一些新的方法,如