1、char[]、String、StringBuffer、StringBuilder
1.1、char[]和String
在 Java 中,String
和 char[]
都用来表示字符串,但它们有一些重要的区别。
String:
- 特点:
String
是一个不可变的类,一旦创建,其内容就不能被修改。String
对象的值在创建后不能被修改,任何对String
类的操作都会返回一个新的String
对象。String
对象是线程安全的,可以安全地被多个线程共享。
- 常用方法:
int length()
: 返回String
的长度。char charAt(int index)
: 返回指定索引处的字符。String substring(int beginIndex, int endIndex)
: 返回一个新的字符串,它是此字符串的一个子字符串。boolean equals(Object anObject)
: 将此字符串与指定对象进行比较,如果是字符串且内容相同,则返回true
。boolean contains(CharSequence s)
: 判断此字符串中是否包含指定的字符序列。String toLowerCase()
: 将字符串转换为小写形式。String toUpperCase()
: 将字符串转换为大写形式。String trim()
: 返回字符串的副本,删除了前导空白和尾部空白。String replace(CharSequence target, CharSequence replacement)
: 将此字符串中的指定字符序列替换为另一个字符序列。
char[]:
- 特点:
char[]
是一个可变的字符数组,可以通过修改数组元素来修改字符串的内容。char[]
的长度可以根据需要进行调整。char[]
是一个原始类型的数组,因此在底层的实现上更接近硬件层。
- 常用方法:
- 由于
char[]
是一个原始类型的数组,因此它没有定义像String
类那样的常用方法。通常,对char[]
进行操作时,需要使用循环、索引访问等方法来实现。
- 由于
示例代码:
public class Main {
public static void main(String[] args) {
// 使用 String 类的常用方法
String str = "Hello, World!";
System.out.println("Length: " + str.length());
System.out.println("Character at index 7: " + str.charAt(7));
System.out.println("Substring from index 1 to 5: " + str.substring(1, 6));
System.out.println("Is 'World' contained: " + str.contains("World"));
System.out.println("Lowercase: " + str.toLowerCase());
System.out.println("Uppercase: " + str.toUpperCase());
System.out.println("Trimmed: " + str.trim());
System.out.println("Replaced: " + str.replace("World", "Java"));
// 使用 char[] 类型的数组
char[] charArray = {'H', 'e', 'l', 'l', 'o'};
System.out.println("Length of charArray: " + charArray.length);
for (char c : charArray) {
System.out.print(c);
}
System.out.println();
}
}
1.2、String、StringBuffer和StringBuilder
在Java中,String
、StringBuffer
和 StringBuilder
都用于处理字符串,但它们之间有一些重要的区别:
String:
- 不可变性:
String
类是不可变的,一旦创建了字符串对象,其内容就不能被修改。对String
的任何修改操作都会返回一个新的String
对象。
- 线程安全性:
String
是线程安全的,可以安全地被多个线程共享。
- 性能:
- 由于
String
是不可变的,对String
进行频繁的修改操作会产生大量的临时对象,导致内存开销较大。
- 由于
- 适用场景:
- 适用于字符串常量或者少量修改的情况,例如配置、属性文件和少量的字符串操作。
StringBuffer:
- 可变性:
StringBuffer
是可变的字符串序列,它允许对字符串进行修改操作,而不会创建新的对象。
- 线程安全性:
StringBuffer
是线程安全的,它的所有公共方法都是同步的,可以安全地被多个线程共享。
- 性能:
StringBuffer
的性能比String
要好,特别是在需要频繁修改字符串内容的情况下,因为它避免了创建大量的临时对象。
- 适用场景:
- 适用于多线程环境下的字符串操作,或者需要频繁进行字符串修改操作的情况。
StringBuilder:
- 可变性:
StringBuilder
也是可变的字符串序列,与StringBuffer
类似,它允许对字符串进行修改操作。
- 线程安全性:
StringBuilder
是非线程安全的,它的所有方法都不是同步的,不适合在多线程环境中使用。
- 性能:
StringBuilder
的性能比StringBuffer
更高,因为它省略了同步操作,但是不适合在多线程环境下使用。
- 适用场景:
- 适用于单线程环境下的字符串操作,或者不需要考虑线程安全性的情况。
总结:
- 如果需要频繁进行字符串操作,并且在多线程环境中使用,应该优先选择
StringBuffer
。 - 如果在单线程环境下进行字符串操作,并且不需要考虑线程安全性,可以选择
StringBuilder
。 - 如果字符串是不可变的,或者只是做少量的字符串操作,应该使用
String
。
1.3、StringBuffer和StringBuilder常用方法
StringBuffer
和 StringBuilder
类都提供了一系列用于字符串操作的方法。它们之间的主要区别在于线程安全性,StringBuffer
是线程安全的,而 StringBuilder
则不是。以下是它们常用的方法:
共同方法:
append(String str)
:将指定的字符串追加到此字符序列。append(int i)
:将指定的整数追加到此字符序列。append(char c)
:将指定的字符追加到此字符序列。insert(int offset, String str)
:将指定的字符串插入此字符序列中。insert(int offset, char c)
:将指定的字符插入此字符序列中。delete(int start, int end)
:删除此字符序列的子字符串。deleteCharAt(int index)
:删除指定位置的字符。reverse()
:反转此字符序列。
仅 `StringBuffer` 的方法:
setCharAt(int index, char ch)
:将指定索引处的字符设置为指定的字符。charAt(int index)
:返回指定索引处的字符。setLength(int newLength)
:设置此字符序列的长度。substring(int start, int end)
:返回一个新的字符串,它包含此字符序列当前所包含的字符子序列。
仅 `StringBuilder` 的方法:
StringBuilder
类不提供额外的方法,除了上面提到的共同方法外,它和 StringBuffer
的用法是一样的。
示例代码:
public class Main {
public static void main(String[] args) {
// 使用 StringBuffer
StringBuffer stringBuffer = new StringBuffer("Hello");
stringBuffer.append(" World"); // 追加字符串
stringBuffer.insert(5, " Java"); // 在指定位置插入字符串
stringBuffer.delete(5, 10); // 删除指定位置的子字符串
stringBuffer.reverse(); // 反转字符串
System.out.println("StringBuffer: " + stringBuffer);
// 使用 StringBuilder
StringBuilder stringBuilder = new StringBuilder("Hello");
stringBuilder.append(" World"); // 追加字符串
stringBuilder.insert(5, " Java"); // 在指定位置插入字符串
stringBuilder.delete(5, 10); // 删除指定位置的子字符串
stringBuilder.reverse(); // 反转字符串
System.out.println("StringBuilder: " + stringBuilder);
}
}
2、数组 和 ArrayList
在 Java 中,
ArrayList
和数组(Array)是两种不同的数据结构,它们有一些区别:
- 大小可变性:
- 数组的长度是固定的,在创建数组时需要指定长度,且长度不能动态改变。
ArrayList
的大小是动态可变的,可以根据需要动态增加或减少元素。
- 类型灵活性:
- 数组可以存储基本数据类型(如 int、double、char 等)和对象类型(如自定义的类对象等)。
ArrayList
只能存储对象类型,不能存储基本数据类型,但可以通过自动装箱和拆箱来存储基本数据类型的值。
- 功能和方法:
- 数组是一个简单的数据结构,提供了一些基本的方法来操作数组元素,如访问、修改、遍历等。
ArrayList
是java.util
包中的一个类,提供了丰富的方法和功能,如添加元素、删除元素、获取元素、搜索、排序等,使得操作更加方便和灵活。
- 性能:
- 在访问元素时,数组的性能比
ArrayList
更好,因为数组是连续存储的,可以通过索引直接访问元素。- 在增删元素时,
ArrayList
的性能比数组更好,因为ArrayList
内部使用了动态数组实现,可以根据需要自动扩容和收缩,并且提供了高效的添加和删除操作。 总的来说,如果需要一个固定大小的、性能更好的数据结构,可以使用数组;如果需要一个动态大小的、功能更丰富的数据结构,可以使用ArrayList
。
在 Java 中,可以使用一些方法来实现数组和 ArrayList 之间的互相转换:
// 数组转换为 ArrayList:
// 使用 Arrays 类的 asList 方法
String[] array = {"A", "B", "C"};
ArrayList list = new ArrayList<>(Arrays.asList(array));
// ArrayList 转换为数组:
ArrayList<String> list = new ArrayList<>();
String[] array = list.toArray(new String[0]);
2.1、数组
2.1.1、常用语法
// 声明 首选方法
dataType[] arrayRefVar;
// 也可以这样声明,但不建议
dataType arrayRefVar[]; // 效果相同,但不是首选方法
// 创建
arrayRefVar = new dataType[arraySize];
// 也可以这样初始化
dataType[] arrayRefVar = {value0, value1, ..., valuek};
// 赋值
arrayRefVar[9] = 11123;
// 获取长度属性
arrayRefVar.length
// 遍历
for (int i = 0; i < myList.length; i++) {
System.out.println(myList[i] + " ");
}
for(type element: array) {
System.out.println(element);
}
// 作为参数传递
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
// 作为返回值
public static int[] reverse(int[] list) {
int[] result = new int[list.length];
return result;
}
// 多维数组
String[][] str = new String[3][4];
2.1.2、Arrays工具类
方法名 | 说明 |
---|---|
public static int binarySearch(Object[] a, Object key) | 用二分查找算法在给定数组中搜索给定值的对象调用前,传入的数组必须是已经排序好的返回索引,若查找不到,返回-1 |
public static boolean equals(long[] a, long[] a2) | 通过 equals 方法比较数组中元素值是否相等 |
public static void fill(int[] a, int val) | 给数组赋值 |
public static void sort(Object[] a) | 对数组排序 |
2.2、ArrayList
// 定义
List<String> sites = new ArrayList<String>(10);
// 添加元素
sites.add("Google");
sites.set(2, "Wiki");
// 获取元素
sites.get(1)
// 删除
sites.remove(3); // 删除第四个元素
// 长度,注意⚠️
sites.size()
// 排序
Collections.sort(sites);
3、LinkedList
LinkedList
是 Java 中的一个类,用于实现双向链表数据结构。在 java.util
包中,它实现了 List
接口,因此可以作为列表使用。下面是一些关键的特点和方法:
特点:
- 双向链表:每个节点都包含了指向前一个节点和后一个节点的引用。
- 动态大小:可以根据需要动态地增加或删除元素,而不需要重新分配内存空间。
- 非同步:
LinkedList
不是线程安全的,如果多个线程同时访问一个LinkedList
实例,并且至少有一个线程修改了列表的结构,则必须在外部进行同步。
主要方法:
- 添加元素:
void addFirst(E e)
: 在列表的开头添加指定的元素。void addLast(E e)
: 在列表的末尾添加指定的元素。void add(int index, E element)
: 在指定的位置插入指定的元素。
- 获取元素:
E getFirst()
: 返回列表的第一个元素。E getLast()
: 返回列表的最后一个元素。E get(int index)
: 返回列表中指定位置的元素。
- 移除元素:
E removeFirst()
: 移除并返回列表的第一个元素。E removeLast()
: 移除并返回列表的最后一个元素。E remove(int index)
: 移除并返回列表中指定位置的元素。
- 其他方法:
int size()
: 返回列表中的元素数。boolean isEmpty()
: 如果列表不包含任何元素,则返回 true。
示例代码:
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
// 添加元素
linkedList.add("Apple");
linkedList.add("Banana");
linkedList.addLast("Orange");
// 获取元素
System.out.println("First element: " + linkedList.getFirst());
System.out.println("Last element: " + linkedList.getLast());
System.out.println("Element at index 1: " + linkedList.get(1));
// 移除元素
linkedList.removeFirst();
linkedList.removeLast();
System.out.println("Size after removal: " + linkedList.size());
}
}
4、HashMap
HashMap
是 Java 中的一个类,用于实现基于哈希表的键值对存储结构。在 java.util
包中,它实现了 Map
接口,因此可以用于存储键值对。下面是一些关键的特点和方法:
特点:
- 键值对存储:
HashMap
存储的是一组键值对(key-value pairs),每个键对应一个值。 - 哈希表:
HashMap
内部使用哈希表来存储键值对,这使得对键的查找操作具有很高的效率。 - 非同步:
HashMap
不是线程安全的,如果多个线程同时访问一个HashMap
实例,并且至少有一个线程修改了映射,则必须在外部进行同步。
主要方法:
- 添加键值对:
V put(K key, V value)
: 将指定的值与指定的键关联,并将其插入到HashMap
中。
- 获取值:
V get(Object key)
: 返回与指定键关联的值,如果该键不存在,则返回null
。
- 移除键值对:
V remove(Object key)
: 移除指定键的映射关系,并返回其关联的值。
- 其他方法:
boolean containsKey(Object key)
: 如果HashMap
包含指定键的映射关系,则返回true
。boolean containsValue(Object value)
: 如果HashMap
中包含一个或多个键映射到指定值,则返回true
。int size()
: 返回HashMap
中的键值对数量。boolean isEmpty()
: 如果HashMap
不包含键值对,则返回true
。void clear()
: 移除HashMap
中的所有键值对。
示例代码:
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
// 创建一个 HashMap 实例
HashMap<String, Integer> hashMap = new HashMap<>();
// 添加键值对
hashMap.put("apple", 10);
hashMap.put("banana", 20);
hashMap.put("orange", 30);
// 获取值
int value = hashMap.get("apple");
System.out.println("Value for key 'apple': " + value);
// 移除键值对
hashMap.remove("banana");
System.out.println("Size after removal: " + hashMap.size());
// 遍历键值对
for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
}
}
5、HashSet
HashSet
是 Java 中的一个类,用于实现集合数据结构,它基于哈希表实现。在 java.util
包中,它实现了 Set
接口,因此可以用于存储不重复的元素。下面是一些关键的特点和方法:
特点:
- 不重复性:
HashSet
中不允许存储重复的元素,如果尝试添加重复的元素,那么新的元素不会被添加到集合中。 - 无序性:
HashSet
中的元素没有固定的顺序,不保证存储顺序与添加顺序一致。 - 基于哈希表:
HashSet
内部使用哈希表来存储元素,这使得对元素的查找操作具有很高的效率。
主要方法:
- 添加元素:
boolean add(E e)
: 将指定的元素添加到集合中,如果元素已存在,则不会添加,并返回false
。
- 移除元素:
boolean remove(Object o)
: 从集合中移除指定的元素,如果元素存在且成功被移除,则返回true
。
- 检查是否包含元素:
boolean contains(Object o)
: 如果集合中包含指定的元素,则返回true
。
- 其他方法:
int size()
: 返回集合中的元素个数。boolean isEmpty()
: 如果集合不包含任何元素,则返回true
。void clear()
: 清空集合中的所有元素。
示例代码:
import java.util.HashSet;
public class Main {
public static void main(String[] args) {
// 创建一个 HashSet 实例
HashSet<String> hashSet = new HashSet<>();
// 添加元素
hashSet.add("apple");
hashSet.add("banana");
hashSet.add("orange");
hashSet.add("apple"); // 重复的元素,不会被添加
// 检查是否包含元素
System.out.println("Contains 'apple': " + hashSet.contains("apple"));
System.out.println("Contains 'grape': " + hashSet.contains("grape"));
// 移除元素
hashSet.remove("banana");
// 打印集合大小
System.out.println("Size: " + hashSet.size());
// 遍历集合
for (String element : hashSet) {
System.out.println(element);
}
}
}
6、Stack
Stack(栈)是一种常见的线性数据结构,它遵循后进先出(LIFO)的原则,即最后入栈的元素将被最先弹出。Java 提供了 Stack 类来表示栈,它位于 java.util
包中,继承自 Vector 类。
Stack 类的主要方法:
- 压栈操作:
void push(E item)
: 将指定的元素压入栈顶。
- 弹栈操作:
E pop()
: 移除并返回栈顶的元素。
- 查看栈顶元素:
E peek()
: 返回栈顶的元素,但不移除。
- 判断栈是否为空:
boolean empty()
: 判断栈是否为空。
- 查找元素在栈中的位置:
int search(Object o)
: 查找指定元素在栈中的位置,如果存在返回其距离栈顶的位置(栈顶为第一个元素,索引为1),否则返回 -1。
示例代码:
import java.util.Stack;
public class Main {
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
// 压栈操作
stack.push(1);
stack.push(2);
stack.push(3);
// 弹栈操作
int poppedElement = stack.pop();
System.out.println("Popped element: " + poppedElement);
// 查看栈顶元素
int peekedElement = stack.peek();
System.out.println("Peeked element: " + peekedElement);
// 判断栈是否为空
boolean isEmpty = stack.empty();
System.out.println("Is stack empty? " + isEmpty);
// 查找元素在栈中的位置
int position = stack.search(2);
System.out.println("Position of element 2: " + position);
// 输出栈中的元素
System.out.println("Stack: " + stack);
}
}
7、Queue
在 Java 中,Queue(队列)是一种常见的线性数据结构,它遵循先进先出(FIFO)的原则,即先进入队列的元素将被最先移除。Java 提供了 Queue 接口作为队列的抽象表示,它位于 java.util
包中。
Queue 接口的主要方法:
- 添加元素:
boolean add(E e)
: 将指定的元素添加到队列尾部,如果队列已满则抛出异常。boolean offer(E e)
: 将指定的元素添加到队列尾部,如果队列已满则返回 false。
- 移除元素:
E remove()
: 移除并返回队列头部的元素,如果队列为空则抛出异常。E poll()
: 移除并返回队列头部的元素,如果队列为空则返回 null。
- 获取头部元素:
E element()
: 返回队列头部的元素,但不移除,如果队列为空则抛出异常。E peek()
: 返回队列头部的元素,但不移除,如果队列为空则返回 null。
Java 中的 Queue 实现类:
Java 提供了几个常用的 Queue 实现类,包括:
- LinkedList:
java.util.LinkedList
类实现了 Queue 接口,可以作为队列使用。它支持快速的插入和删除操作,但不支持并发访问。 - PriorityQueue:
java.util.PriorityQueue
类实现了 Queue 接口,是一个基于优先级堆的无界队列。它按照元素的优先级顺序来确定出队顺序。 - ArrayDeque:
java.util.ArrayDeque
类实现了 Queue 接口,是一个基于数组的双端队列。它可以作为栈、队列或双端队列使用。
示例代码:
import java.util.Queue;
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
// 添加元素到队列
queue.add("A");
queue.add("B");
queue.add("C");
// 获取并移除队列头部的元素
String first = queue.poll();
System.out.println("Removed element: " + first);
// 获取队列头部的元素(不移除)
String peeked = queue.peek();
System.out.println("Peeked element: " + peeked);
// 输出队列中的元素
System.out.println("Queue: " + queue);
}
}
在 Java 中,Queue
是一个接口,而接口是不能被直接实例化的。所以不能直接使用 new Queue<>()
来创建一个 Queue
实例。你必须使用一个实现了 Queue
接口的类来创建一个队列实例,比如 LinkedList
、ArrayDeque
或者 PriorityQueue
。因此,你可以这样做:
Queue<String> queue = new LinkedList<>();
Queue<String> queue = new ArrayDeque<>();
Queue<String> queue = new PriorityQueue<>();
以上三种方式都可以创建一个 Queue<String>
的实例,分别使用了 LinkedList
、ArrayDeque
和 PriorityQueue
这三个实现类。
8、PriorityQueue
在 Java 中,优先队列(PriorityQueue)是一种特殊的队列,它按照元素的优先级顺序来确定出队顺序。具体来说,优先队列中的元素必须是可比较的,并且根据它们的自然顺序或者通过提供的 Comparator 进行比较。
创建优先队列:
import java.util.PriorityQueue;
public class Main {
public static void main(String[] args) {
// 创建一个空的优先队列
PriorityQueue<Integer> pq1 = new PriorityQueue<>();
// 创建一个带有初始容量的优先队列,并使用自然排序
PriorityQueue<Integer> pq2 = new PriorityQueue<>(10);
// 创建一个带有初始容量和自定义比较器的优先队列
PriorityQueue<Integer> pq3 = new PriorityQueue<>(10, (a, b) -> b - a);
// 添加元素到优先队列中
pq1.add(5);
pq1.add(3);
pq1.add(7);
// 输出优先队列中的元素(不按顺序)
System.out.println("PriorityQueue 1: " + pq1);
}
}
常用操作:
add(e)
或offer(e)
: 向队列中添加元素。peek()
: 获取队列中优先级最高的元素,但不移除。poll()
: 获取并移除队列中优先级最高的元素。remove(Object o)
: 移除队列中指定的元素。isEmpty()
: 判断队列是否为空。size()
: 获取队列中元素的个数。
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.add(5);
pq.add(3);
pq.add(7);
System.out.println("Peek: " + pq.peek()); // 输出 3,最高优先级元素
System.out.println("Poll: " + pq.poll()); // 输出 3,并移除
System.out.println("Queue size: " + pq.size()); // 输出 2,队列中剩余元素个数
注意事项:
- 优先队列中的元素必须是可比较的,如果元素类型不支持比较,添加到优先队列中时会抛出
ClassCastException
异常。 - 当使用自定义对象作为元素时,确保实现了
Comparable
接口或者提供了比较器(Comparator)。 - 优先队列不允许添加
null
元素。
优先队列常用于实现任务调度、事件排序等场景,它可以确保按照优先级顺序处理任务或事件。
评论区
0/2048