Java Collection Framework 介绍和例子

58 阅读3分钟

说明

由于之前主要使用golang,工作中需要使用到java,之前都是比较零碎的学习java的一些八股文,现在打算系统性的学习java并做一些记录,这一篇Java Colleciton Framework相关的就当做一个开始

Java Collection Framework 介绍

Java Collection Framework 是Java标准库中的一个重要部分,它提供了一套统一的架构来存储和操作对象组。这个框架包含了许多接口、实现类和算法,使得开发者能够更加高效地处理数据集合。

主要组成部分

接口(Interfaces):定义了集合的抽象数据类型。主要接口包括:

  • Iterable
  • Collection
  • List
  • Set
  • Queue
  • Map(技术上不是Collection的子接口,但通常被视为集合框架的一部分)

整体类关系图,CollecitonMap是两个顶层接口,两者都继承了Iterator接口

java_collections_overview.png

Collection相关的接口

Collection 接口

  • 继承自 Iterable 接口
  • 是 List、Set 和 Queue 的父接口
  • 定义了集合的基本操作,如 add(), remove(), contains() 等
List的具体例子

ArrayListLinkedList

// 初始化
List<Integer> al =  new ArrayList<>(10);
System.out.println(al.size());
// add
al.add(100);

// 使用实现collection接口的集合初始化
List<Integer> ll = new LinkedList<>(al);
System.out.println(ll.size());
// forEach, iterator
ll.forEach(System.out::println);
ll.add(101);

// get by index, ArrayList和LinkedList都是支持的
al.get(0); 
ll.get(1); 

// set
al.set(0, 100);
ll.set(1, 1000);

// clear
al.clear();
ll.clear();

// remove by index
int targetIdx = 0;
Integer tv = ll.remove(targetIdx); // 如果targetIdx 超出了list的范围会有java.lang.IndexOutOfBoundsException 异常抛出

// remove by element
Integer target = 101;
boolean success = ll.remove(target); // 如果target不存在,也不会抛异常,用success来做判断就行了

// contains
boolean exists = ll.contains(100);
// indexOf和lastIndexOf 
ll.indexOf(100);
ll.lastIndexOf(1000);

// addAll, removeAll, containsAll
List<Interger> opCol = List.of(1, 2, 3, 4);
ll.addAll(opCol);
ll.removeAll(opCol);
ll.containsAll(opCol);

// sort
al.sort(Integer::compareTo);
ll.sort(Integer::compareTo);

// iterator
al.clear();
al.addAll(List.of(1, 2, 3, 4));
Iterator<Integer> ai = al.iterator();
while (ai.hasNext()) {
    System.out.println(ai.next());
}

// listIterator, 可以前向遍历
ListIterator<Integer> ali = al.listIterator(al.size());
while(ali.hasPrevious()) {
    System.out.printf("%d ", ali.previous());
}
System.out.println();

// subList 浅拷贝
List<Integer> sub = ll.subList(1, ll.size());
sub.set(0, 1000);
System.out.println(Arrays.toString(sub.toArray())); // [1000, 3, 4]
System.out.println(Arrays.toString(ll.toArray()));  // [1, 1000, 3, 4]

// SplitIterator
List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elephant");
Spliterator<String> spliterator = fruits.spliterator();
// trySplit之后返回的spliterator是原来数据的前面部分,老的spliterator继承了下半部分
Spliterator<String> splitSpliterator = spliterator.trySplit();

splitSpliterator.forEachRemaining(System.out::println); // Apple, Banana

spliterator.forEachRemaining(System.out::println); // Cherry, Date, Elephant
Queue相关的例子
// LinkedList Queue
Queue<Integer> lq = new LinkedList<>(List.of(1, 2, 3, 4));
System.out.println(lq.size());
boolean success = lq.add(5);
System.out.println("add result: " + success);
success = lq.offer(6);
System.out.println("offer result: " + success);
Integer top = lq.peek();
System.out.println("top: " + top);
System.out.println("before poll queue size: " + lq.size());
top = lq.poll();
System.out.println("after poll queue size: " + lq.size() + ", top: " + top);

// PriorityQueue
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.offer(3);
pq.offer(2);
pq.offer(1);

Integer pqTop = pq.poll();
System.out.println("top: " + pqTop + ", pq size: " + pq.size());

// LinkedBlockingQueue
LinkedBlockingQueue<Integer> linkedBlockingQueue = new LinkedBlockingQueue<>(2);
// 非阻塞方法
linkedBlockingQueue.offer(1);
linkedBlockingQueue.offer(2);
success = linkedBlockingQueue.offer(3);
System.out.println("over capacity add success: " + success);
try {
    success = linkedBlockingQueue.offer(3, 1, TimeUnit.SECONDS);
    System.out.println("timeout offer returns: " + success);
} catch (InterruptedException e) {
    System.out.println("offer timeout");
    e.printStackTrace();
}
// 非阻塞方法
linkedBlockingQueue.poll();
try {
    linkedBlockingQueue.take();
    pqTop = linkedBlockingQueue.poll(1, TimeUnit.SECONDS);
    System.out.println("poll with timeout returns: " + pqTop);
} catch (InterruptedException e) {
    e.printStackTrace();
}

// 阻塞方法put和take示例
LinkedBlockingQueue<Integer> linkedBlockingQueue = new LinkedBlockingQueue<>(1);
Thread thread1 = new Thread(() -> {
    try {
        Thread.sleep(1000);
        Integer top = linkedBlockingQueue.take();
        System.out.println("top: " + top);
    } catch (InterruptedException e) {
        // pass
    }
});

Thread thread2 = new Thread(() -> {
    try {
        long start = System.currentTimeMillis();
        linkedBlockingQueue.put(1);
        System.out.println("first put consumes: " + (System.currentTimeMillis() - start));
        start = System.currentTimeMillis();
        linkedBlockingQueue.put(2);
        System.out.println("second put consumes: " + (System.currentTimeMillis() - start));
    } catch (InterruptedException e) {
        // pass
    }
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();

// Deque
Deque<Integer> dq = new LinkedList<>();
dq.offerLast(1);
dq.offer(2);
dq.offerFirst(3);
Integer top = dq.peek();
System.out.println("top(peek, peekFirst):" + top);
top = dq.peekLast();
System.out.println("peekLast: " + top);
while (!dq.isEmpty()) {
    System.out.println(dq.pollLast());
}
Set相关的例子
Set<Integer> s1 = new HashSet<>(List.of(1, 1, 2, 2, 3, 3));
System.out.println(s1.size());
Iterator iterator = s1.iterator();
while(iterator.hasNext()) {
    System.out.println(iterator.next());
}
System.out.println(s1.contains(4));
System.out.println(s1.contains(2));

TreeSet<Integer> treeSet = new TreeSet<>(List.of(1, 2, 3, 4, 5, 6));
for (Integer v: treeSet) {
    System.out.println("treeSet member: " + v);
}
System.out.println("treeSet first: " + treeSet.first());
System.out.println("treeSet last: " + treeSet.last());
SortedSet<Integer> tailSet = treeSet.tailSet(4);

for (Integer v: tailSet) {
    System.out.println("tailSet: " + v);
}
SortedSet<Integer> headSet = treeSet.headSet(4);
for (Integer v: headSet) {
    System.out.println("headSet: " + v);
}

TreeSet<Integer> treeSet1 = new TreeSet<>(Comparator.reverseOrder());
treeSet1.addAll(List.of(1, 2, 3, 4, 5, 6));
for(Integer v: treeSet1) {
    System.out.println("reverse set: " + v);
}

Map相关的接口

HashMap的例子
Map<Integer, Integer> map = new HashMap<>();
map.put(1, 11);
map.put(2, 22);
System.out.println(map.containsKey(1));
// O(n)
System.out.println(map.containsValue(11));
for(Map.Entry<Integer, Integer> entry: map.entrySet()) {
    System.out.println("key: " + entry.getKey() + ", value: " + entry.getValue());
}
Integer oldValue = map.remove(1);
System.out.println("oldValue: " + oldValue);

TreeMap相关的例子
TreeMap<Integer, Integer> treeMap = new TreeMap<>();
treeMap.put(3, 33);
treeMap.put(2, 22);
treeMap.put(1, 11);
for(Map.Entry<Integer, Integer> entry: treeMap.entrySet()) {
    System.out.println("key: " + entry.getKey() + ", value: " + entry.getValue());
}
Integer lowerKey = treeMap.lowerKey(2);
System.out.println("lowerKey: " + lowerKey);
Integer higherKey = treeMap.higherKey(2);
System.out.println("higherKey: " + higherKey);
Integer floorKey = treeMap.floorKey(2);
Integer ceilingKey = treeMap.ceilingKey(2);
System.out.printf("floorKey = %d, ceilingKey = %d\n", floorKey, ceilingKey);

能保证插入顺序的集合

// LinkedHashSet, LinkedHashMap, LinkedHashSet底层和LinkedHashMap一致
LinkedHashMap<Integer, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put(1, 2);
linkedHashMap.put(2, 3);
linkedHashMap.put(3, 4);
for (var e : linkedHashMap.entrySet()) {
    System.out.println("key = " + e.getKey() + ", value = " + e.getValue());
}

底层数据结构

  1. ArrayList: 数组

  2. LinkedList: 链表

  3. PriorityQueue: 堆

  4. HashSet和HashMap: 都是基于HashMap

  5. TreeMap: 红黑树

  6. LinkedHashSet和LinkedHashMap:基于HashMap,然后使用链表保存了插入顺序

算法(Algorithms):这些是在集合上执行的方法,如搜索和排序。它们通常以静态方法的形式存在于Collections类中。

// sort
List<Integer> numbers = Arrays.asList(2, 1, 3, 4, 5);
Collections.sort(numbers);
System.out.println(Arrays.toString(numbers.toArray()));
Collections.sort(numbers, Collections.reverseOrder());
System.out.println(Arrays.toString(numbers.toArray()));

// binarySearch
int idx = Collections.binarySearch(numbers, 1);
System.out.println("1 idx: " + idx);
idx = Collections.binarySearch(numbers, 10);
System.out.println("10 idx: " + idx);

// shuffle
Collections.shuffle(numbers);
System.out.println(Arrays.toString(numbers.toArray()));

// reverse
Collections.reverse(numbers);
System.out.println(Arrays.toString(numbers.toArray()));



参考资料

  1. pdai.tech/md/java/col…