算法竞赛Java数据结构与算法类详解

CONTENTS

    • 1. String/StringBuffer/StringBuilder
    • 2. Arrays
    • 3. ArrayList
    • 4. LinkedList
    • 5. HashSet
    • 6. HashMap

1. String/StringBuffer/StringBuilder

String 类即字符串,在 Java 中 String 类是不可改变的,如果修改 String 对象,那么其实是开一个新的空间保存,而原空间中的字符串还存在于内存中。String 类的用法如下:

// 定义
String str = "Hello World!";

char[] charArray = { 'A', 'B', 'C' };
String strFromChar = new String(charArray);

// 遍历
for (int i = 0; i < str.length(); i++)
    System.out.print(str.charAt(i));
System.out.println();

for (char c: str.toCharArray())  // toCharArray将String转为字符数组
    System.out.print(c);
System.out.println();

// 常用方法
str.charAt(0);  // H,返回第0个字符
str.compareTo("G");  // 1,比较字符串,如果大于返回1,小于返回-1,否则返回0
str.equals("Hello World!");  // true,Java中比较两个字符串值是否相等必须用equals
str.startsWith("He");  // true,判断是否以"He"开头
str.endsWith("d!");  // true,判断是否以"d!"结尾
str.indexOf('o');  // 4,返回'o'第一次出现的位置
str.indexOf('o', 5);  // 7,返回'o'从下标5开始第一次出现的位置
str.lastIndexOf('o');  // 7,返回'o'最后出现的位置
str.lastIndexOf('o', 6);  // 4,返回'o'在下标6之前的字符串中最后出现的位置
str.replace('o', 'x');  // Hellx Wxrld!,将'o'替换成'x'
str.split(" ");  // [Hello, World!],以" "进行分割,返回分割后的数组,支持正则表达式
str.substring(6, 9);  // Wor,返回[6, 9)的字串,如果不指定第二个下标默认为末尾
str.toLowerCase();  // hello world!,将所有字母转为小写
str.toUpperCase();  // HELLO WORLD!,将所有字母转为大写

当需要对字符串进行频繁修改时,要用 StringBufferStringBuilder 类。这两个类的对象能够被多次修改,并且不产生新的未使用对象。

StringBufferStringBuilder 之间最大的不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。但是 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。以 StringBuilder 为例,用法如下:

// 定义
StringBuilder strBuilder = new StringBuilder(str);

// 遍历
for (int i = 0; i < strBuilder.length(); i++)
    System.out.print(strBuilder.charAt(i));
System.out.println();

// 常用方法
strBuilder.append("A");  // 在末尾追加字符串/字符/数字
strBuilder.reverse();  // 翻转字符串
strBuilder.delete(0, 1);  // 删除[0, 1)中的字符串
strBuilder.insert(6, "A");  // 在下标6的位置插入字符串/字符/数字
strBuilder.replace(0, 7, "Hello");  // 将[0, 7)中的字符串替换为"Hello"
strBuilder.toString();  // 转换回String

2. Arrays

Arrays 类定义在 java.util 包中,该类能够方便地操作数组,用法如下:

// 定义
int[] arr = { 2, 4, 3, 1, 5 };
int[][] arr2d = {{ 1, 2 }, { 3, 4 }};
Integer[] arrInteger = { 2, 4, 3, 1, 5 };

// 遍历
for (int i = 0; i < arr.length; i++)
    System.out.print(arr[i] + " ");
System.out.println();

for (int x: arr)
    System.out.print(x + " ");
System.out.println();

// 常用方法
Arrays.sort(arr);  // 从小到大排序
Arrays.sort(arrInteger, (x, y) -> y - x);  // 从大到小排序,必须用Integer对象
Arrays.binarySearch(arr, 3);  // 返回2,对排好序的数组二分查找
Arrays.equals(arr, arr);  // 返回true,判断两个数组是否相等
Arrays.fill(arr, 0);  // 用0填充数组所有元素
Arrays.toString(arr);  // 返回[0, 0, 0, 0, 0],将数组的第一维转换成字符串形式
Arrays.deepToString(arr2d);  // 返回[[1, 2], [3, 4]],递归转换数组的每一维,可用于二维及以上的数组

3. ArrayList

Java 的集合框架主要包括两种类型的容器:

  • 集合(Collection):存储一个元素集合。
  • 图(Map):存储键/值对映射。

其中,Collection 接口又包含了 List 接口和 Set 接口两大分支,前者为允许存在重复元素的有序集合,后者为不允许存在重复元素的无序集合;Map 是一个映射接口,其中的每一个元素包含一个 key 和对应的 value

ArrayList 类实现了 List 接口,继承自 AbstractList 类,位于 java.util 包中(迭代器也位于该包中),可以用于动态地调整存储在其中的元素的大小。ArrayList 类是基于数组的数据结构,它提供了一组用于操作元素的方法,包括添加、删除、插入、搜索和排序等。

ArrayList 用法如下:

// 定义
ArrayList<Integer> v = new ArrayList<>();  // <>中只能为引用数据类型
ArrayList<Integer> v = new ArrayList<>(Arrays.asList(1, 2, 3));  // 初始化时赋值,当前为[1, 2, 3]

// 遍历
for (int i = 0; i < v.size(); i++)  // 传统for循环
    System.out.printf("%d ", v.get(i));
System.out.println();

for (int x: v)  // forEach循环,推荐这种写法
    System.out.printf("%d ", x);
System.out.println();

for (Iterator<Integer> it = v.iterator(); it.hasNext();)  // 迭代器
    System.out.printf("%d ", it.next());
System.out.println();

for (ListIterator<Integer> it = v.listIterator(); it.hasNext();)  // 列表迭代器
    System.out.printf("%d ", it.next());
System.out.println();

v.stream().forEach(x -> System.out.printf("%d ", x));  // Java8的Stream API和Lambda表达式
System.out.println();

// 常用方法
v.add(4);  // 在末尾添加4,当前为[1, 2, 3, 4]
v.add(1, 5);  // 在下标1处添加5,当前为[1, 5, 2, 3, 4]
v.remove(4);  // 删除下标4处的元素,当前为[1, 5, 2, 3]
v.set(0, 4);  // 将下标0处的元素置为4,当前为[4, 5, 2, 3]
v.subList(0, 2);  // 返回[4, 5],截取[0, 2)的子列表
v.indexOf(5);  // 返回1,获取元素5第一次出现的下标,若不存在返回-1
v.isEmpty();  // 返回false,判断是否为空
v.contains(2);  // 返回true,判断是否含有元素2
v.sort(Comparator.naturalOrder());  // 从小到大排序
v.sort(Comparator.reverseOrder());  // 从大到小排序
ArrayList<Integer> t = (ArrayList<Integer>)v.clone();  // 复制一份,和原ArrayList不是同一份

Integer a[] = new Integer[v.size()];
v.toArray(a);  // 转换为数组

4. LinkedList

Java 的 LinkedList 类是一个实现了 List 接口和 Deque 接口的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。

ArrayList 相比,LinkedList 的增加和删除的操作效率更高,而查找和修改的操作效率较低。因此需要频繁访问列表中的某一个元素且只需要在列表末尾进行添加和删除元素操作推荐使用 ArrayList,而需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作推荐使用 LinkedList

LinkedList 的定义与遍历方式与 ArrayList 相同,且上述的 ArrayList 常用方法在 LinkedList 中均可使用,额外添加的部分方法如下:

// 定义
LinkedList<Integer> v = new LinkedList<>(Arrays.asList(1, 2, 3));

// 常用方法
v.addLast(4);  // 在尾部添加4,当前为[1, 2, 3, 4]
v.addFirst(5);  // 在首部添加5,当前为[5, 1, 2, 3, 4]
v.removeLast();  // 删除并返回尾部元素,当前为[5, 1, 2, 3]
v.removeFirst();  // 删除并返回首部元素,当前为[1, 2, 3]
v.getFirst();  // 返回首部元素1
v.getLast();  // 返回尾部元素3

5. HashSet

Java 的 HashSet 类是一个实现了 Set 接口的集合类,它使用 HashMap 作为基础,因此其特性和 HashMap 类似:

  • 不重复:是一个没有重复元素的集合。
  • 无序:它不保证元素的顺序。
  • 允许 null:允许使用 null 元素。
  • 非同步:即不是线程安全的,如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 必须在多线程访问时显式同步对 HashSet 的并发访问。

HashSet 用法如下:

// 定义
HashSet<Integer> st = new HashSet<>();
HashSet<Integer> st = new HashSet<>(Arrays.asList(1, 2, 3));

// 遍历
for (int x: st)  // forEach循环,推荐这种写法
    System.out.printf("%d ", x);
System.out.println();

for (Iterator<Integer> it = st.iterator(); it.hasNext();)  // 迭代器
    System.out.printf("%d ", it.next());
System.out.println();

st.stream().forEach(x -> System.out.printf("%d ", x));  // Java8的Stream API和Lambda表达式
System.out.println();

// 常用方法
st.add(4);  // 添加元素,当前为{1, 2, 3, 4}
st.remove(3);  // 删除元素,当前为{1, 2, 4}
st.contains(3);  // 返回false,判断是否存在3
st.size();  // 返回2,获取集合的元素数量
st.isEmpty();  // 返回false,判断是否为空
st.clear();  // 清空元素

6. HashMap

Java 的 HashMap 是一个实现了 Map 接口的哈希表,它存储的内容是键值对(key-value)映射。HashMap 的特性如下:

  • 键值对存储:HashMap 是以 key-value 存储形式存在,主要用来存放键值对。
  • 非同步:HashMap 的实现不是同步的,这意味着它不是线程安全的。
  • 允许 null:它的 keyvalue 都可以为 null
  • 无序:此外,HashMap 中的映射不是有序的。

HashMap 的底层数据结构为:

  • Java8 之前:HashMap 由数组+链表组成,数组是 HashMap 的主体,链表则主要用于解决哈希碰撞
  • Java8 之后:在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)并且当前数组的长度大于64时,此时此索引位置上的所有数据改为使用红黑树存储。

HashMap 用法如下:

// 定义
HashMap<String, Integer> mp = new HashMap<>();
HashMap<String, Integer> mp = new HashMap<String, Integer>() {{  // 可能会导致一些未预期的副作用,因为它实际上创建了一个匿名子类
    put("ABC", 1);
    put("DEF", 2);
}};

// 遍历
for (String k: mp.keySet())  // 遍历key
    System.out.println(k);

for (int v: mp.values())  // 遍历value
    System.out.println(v);

for (Map.Entry<String, Integer> e: mp.entrySet())  // 遍历key-value,推荐这种写法
    System.out.println(e.getKey() + " " + e.getValue());

for (Iterator it = mp.entrySet().iterator(); it.hasNext();) {  // 迭代器
    Map.Entry e = (Map.Entry)it.next();
    System.out.println(e.getKey() + " " + e.getValue());
}

// 常用方法
mp.put("YYJ", 666);  // 添加键值对,如果键已经存在,就替换原来的值
mp.get("YYJ");  // 返回666,获取某个key对应的value,如果key不存在返回null
mp.remove("YYJ");  // 删除指定的key及其对应的value,如果key不存在返回null
mp.containsKey("YYJ");  // 返回false,判断是否包含指定的key
mp.containsValue(1);  // 返回true,判断是否包含指定的value
mp.size();  // 返回2,获取键值对的数量
mp.clear();  // 清空键值对
mp.isEmpty();  // 返回true,判断是否为空

你可能感兴趣的:(Java,算法,java,数据结构,学习,笔记)