Java语言刷题必备知识

更新于2024-11-05 08:00:0034 分钟15 千字256873510
摘要

1、char[]、String、StringBuffer、StringBuilder

1.1、char[]和String

在 Java 中,Stringchar[] 都用来表示字符串,但它们有一些重要的区别。

String:

  • 特点​:
    • String 是一个不可变的类,一旦创建,其内容就不能被修改。
    • String 对象的值在创建后不能被修改,任何对 String 类的操作都会返回一个新的 String 对象。
    • String 对象是线程安全的,可以安全地被多个线程共享。
  • 常用方法​:
    1. int length(): 返回 String 的长度。
    2. char charAt(int index): 返回指定索引处的字符。
    3. String substring(int beginIndex, int endIndex): 返回一个新的字符串,它是此字符串的一个子字符串。
    4. boolean equals(Object anObject): 将此字符串与指定对象进行比较,如果是字符串且内容相同,则返回 true
    5. boolean contains(CharSequence s): 判断此字符串中是否包含指定的字符序列。
    6. String toLowerCase(): 将字符串转换为小写形式。
    7. String toUpperCase(): 将字符串转换为大写形式。
    8. String trim(): 返回字符串的副本,删除了前导空白和尾部空白。
    9. String replace(CharSequence target, CharSequence replacement): 将此字符串中的指定字符序列替换为另一个字符序列。

char[]:

  • 特点​:
    • char[] 是一个可变的字符数组,可以通过修改数组元素来修改字符串的内容。
    • char[] 的长度可以根据需要进行调整。
    • char[] 是一个原始类型的数组,因此在底层的实现上更接近硬件层。
  • 常用方法​:
    1. 由于 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中,StringStringBufferStringBuilder 都用于处理字符串,但它们之间有一些重要的区别:

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常用方法

StringBufferStringBuilder 类都提供了一系列用于字符串操作的方法。它们之间的主要区别在于线程安全性,StringBuffer 是线程安全的,而 StringBuilder 则不是。以下是它们常用的方法:

共同方法:

  1. append(String str):将指定的字符串追加到此字符序列。
  2. append(int i):将指定的整数追加到此字符序列。
  3. append(char c):将指定的字符追加到此字符序列。
  4. insert(int offset, String str):将指定的字符串插入此字符序列中。
  5. insert(int offset, char c):将指定的字符插入此字符序列中。
  6. delete(int start, int end):删除此字符序列的子字符串。
  7. deleteCharAt(int index):删除指定位置的字符。
  8. reverse():反转此字符序列。

仅 `StringBuffer` 的方法:

  1. setCharAt(int index, char ch):将指定索引处的字符设置为指定的字符。
  2. charAt(int index):返回指定索引处的字符。
  3. setLength(int newLength):设置此字符序列的长度。
  4. 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)是两种不同的数据结构,它们有一些区别:

  1. 大小可变性:
  • 数组的长度是固定的,在创建数组时需要指定长度,且长度不能动态改变。
  • ArrayList 的大小是动态可变的,可以根据需要动态增加或减少元素。
  1. 类型灵活性:
  • 数组可以存储基本数据类型(如 int、double、char 等)和对象类型(如自定义的类对象等)。
  • ArrayList 只能存储对象类型,不能存储基本数据类型,但可以通过自动装箱和拆箱来存储基本数据类型的值。
  1. 功能和方法:
  • 数组是一个简单的数据结构,提供了一些基本的方法来操作数组元素,如访问、修改、遍历等。
  • ArrayListjava.util 包中的一个类,提供了丰富的方法和功能,如添加元素、删除元素、获取元素、搜索、排序等,使得操作更加方便和灵活。
  1. 性能:
  • 在访问元素时,数组的性能比 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 接口,因此可以作为列表使用。下面是一些关键的特点和方法:

特点:

  1. 双向链表:每个节点都包含了指向前一个节点和后一个节点的引用。
  2. 动态大小:可以根据需要动态地增加或删除元素,而不需要重新分配内存空间。
  3. 非同步:LinkedList 不是线程安全的,如果多个线程同时访问一个 LinkedList 实例,并且至少有一个线程修改了列表的结构,则必须在外部进行同步。

主要方法:

  1. 添加元素:
    • void addFirst(E e): 在列表的开头添加指定的元素。
    • void addLast(E e): 在列表的末尾添加指定的元素。
    • void add(int index, E element): 在指定的位置插入指定的元素。
  2. 获取元素:
    • E getFirst(): 返回列表的第一个元素。
    • E getLast(): 返回列表的最后一个元素。
    • E get(int index): 返回列表中指定位置的元素。
  3. 移除元素:
    • E removeFirst(): 移除并返回列表的第一个元素。
    • E removeLast(): 移除并返回列表的最后一个元素。
    • E remove(int index): 移除并返回列表中指定位置的元素。
  4. 其他方法:
    • 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 接口,因此可以用于存储键值对。下面是一些关键的特点和方法:

特点:

  1. 键值对存储:HashMap 存储的是一组键值对(key-value pairs),每个键对应一个值。
  2. 哈希表:HashMap 内部使用哈希表来存储键值对,这使得对键的查找操作具有很高的效率。
  3. 非同步:HashMap 不是线程安全的,如果多个线程同时访问一个 HashMap 实例,并且至少有一个线程修改了映射,则必须在外部进行同步。

主要方法:

  1. 添加键值对:
    • V put(K key, V value): 将指定的值与指定的键关联,并将其插入到 HashMap 中。
  2. 获取值:
    • V get(Object key): 返回与指定键关联的值,如果该键不存在,则返回 null
  3. 移除键值对:
    • V remove(Object key): 移除指定键的映射关系,并返回其关联的值。
  4. 其他方法:
    • 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 接口,因此可以用于存储不重复的元素。下面是一些关键的特点和方法:

特点:

  1. 不重复性:HashSet 中不允许存储重复的元素,如果尝试添加重复的元素,那么新的元素不会被添加到集合中。
  2. 无序性:HashSet 中的元素没有固定的顺序,不保证存储顺序与添加顺序一致。
  3. 基于哈希表:HashSet 内部使用哈希表来存储元素,这使得对元素的查找操作具有很高的效率。

主要方法:

  1. 添加元素:
    • boolean add(E e): 将指定的元素添加到集合中,如果元素已存在,则不会添加,并返回 false
  2. 移除元素:
    • boolean remove(Object o): 从集合中移除指定的元素,如果元素存在且成功被移除,则返回 true
  3. 检查是否包含元素:
    • boolean contains(Object o): 如果集合中包含指定的元素,则返回 true
  4. 其他方法:
    • 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 类的主要方法:

  1. 压栈操作:
    • void push(E item): 将指定的元素压入栈顶。
  2. 弹栈操作:
    • E pop(): 移除并返回栈顶的元素。
  3. 查看栈顶元素:
    • E peek(): 返回栈顶的元素,但不移除。
  4. 判断栈是否为空:
    • boolean empty(): 判断栈是否为空。
  5. 查找元素在栈中的位置:
    • 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 接口的主要方法:

  1. 添加元素:
    • boolean add(E e): 将指定的元素添加到队列尾部,如果队列已满则抛出异常。
    • boolean offer(E e): 将指定的元素添加到队列尾部,如果队列已满则返回 false。
  2. 移除元素:
    • E remove(): 移除并返回队列头部的元素,如果队列为空则抛出异常。
    • E poll(): 移除并返回队列头部的元素,如果队列为空则返回 null。
  3. 获取头部元素:
    • E element(): 返回队列头部的元素,但不移除,如果队列为空则抛出异常。
    • E peek(): 返回队列头部的元素,但不移除,如果队列为空则返回 null。

Java 中的 Queue 实现类:

Java 提供了几个常用的 Queue 实现类,包括:

  1. LinkedList:java.util.LinkedList 类实现了 Queue 接口,可以作为队列使用。它支持快速的插入和删除操作,但不支持并发访问。
  2. PriorityQueue:java.util.PriorityQueue 类实现了 Queue 接口,是一个基于优先级堆的无界队列。它按照元素的优先级顺序来确定出队顺序。
  3. 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 接口的类来创建一个队列实例,比如 LinkedListArrayDeque 或者 PriorityQueue。因此,你可以这样做:

Queue<String> queue = new LinkedList<>();

Queue<String> queue = new ArrayDeque<>();

Queue<String> queue = new PriorityQueue<>();

以上三种方式都可以创建一个 Queue<String> 的实例,分别使用了 LinkedListArrayDequePriorityQueue 这三个实现类。

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 元素。

优先队列常用于实现任务调度、事件排序等场景,它可以确保按照优先级顺序处理任务或事件。

评论区

你认为这篇文章怎么样?
  • great
    0
  • happy
    0
  • doubt
    0
  • boring
    0
  • bad
    0

0/2048