java 集合

java 集合

数据结构总览

datastruct.png

Collection

Collection 接口主要关注集合的添加,删除,包含

  • isEmpty: 判断是否没有元素
  • size: 获取元素个数
  • add: 添加元素
  • addAll: 添加给定集合中的所有元素,相当于并集
  • remove: 删除元素
  • removeAll: 删除给定集合中的所有元素,相当于差集
  • removeIf: 删除满足谓词的元素
  • retainAll: 保留给定集合中的元素,相当于交集
  • contains: 判断某个元素是否在集合内
  • containsAll: 判断给定集合中的所有元素是否都在集合内
  • clear: 清空所有元素
  • stream: 支持流处理
{
    Collection<Integer> c = new ArrayList<>(List.of(1, 2, 3, 4, 5));
    assertEquals(c.size(), 5);
    assertFalse(c.isEmpty());
    assertTrue(c.contains(3));
    assertTrue(c.containsAll(List.of(2, 4)));
    c.clear();
    assertEquals(c.size(), 0);
    assertTrue(c.isEmpty());
}
{
    Collection<Integer> c = new ArrayList<>(List.of(1, 2, 3, 4, 5));
    c.add(6);
    assertThat(c, equalTo(List.of(1, 2, 3, 4, 5, 6)));
    c.addAll(List.of(7, 8, 9));
    assertThat(c, equalTo(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9)));
}
{
    Collection<Integer> c = new ArrayList<>(List.of(1, 2, 3, 4, 5));
    c.remove(3);
    assertThat(c, equalTo(List.of(1, 2, 4, 5)));
    c.removeAll(List.of(2, 3));
    assertThat(c, equalTo(List.of(1, 4, 5)));
    c.retainAll(List.of(1, 2, 3, 4));
    assertThat(c, equalTo(List.of(1, 4)));
    c.removeIf(x -> x % 2 == 0);
    assertThat(c, equalTo(List.of(1)));
}
{
    Collection<Integer> c = new ArrayList<>(List.of(1, 2, 3, 4, 5));
    c.forEach(System.out::print);
    assertEquals(c.stream().map(x -> x * x).mapToInt(x -> x).sum(), 55);

    for (Integer i : c) {
        System.out.print(i);
    }
}

List

List 接口为顺序表,继承自 Collection,关注集合的定位,查找,修改和排序,底层有两种实现,链表和数组,链表有较好的头部插入性能,数组在随机访问的时候有很大优势,util 里主要提供了三种顺序表:

  • LinkedList: 双链表实现,定位元素需要遍历,get 性能是 O(n);插入性能 O(1),但指定下标插入需要先定位;查找也需要遍历,性能 O(n)
  • ArrayList: 数组实现,插入时需要移动数组中的元素,插入性能是 O(n),向后插入是 O(1),插入时如果数组空间不够,需要重新申请新的空间,并将原来的元素添加到新的数组中;可以根据下标定位元素,支持随机访问,get 性能是 O(1);查找需要遍历,性能 O(n)
  • Vector: 和 ArrayList 底层一样,但是是线程安全的

ListCollection 的基础上,提供了下面接口:

  • get: 按下标定位元素
  • indexOf: 查找元素,返回下标
  • lastIndexOf: 从后向前查找元素
  • subList: 子链表
  • set: 指定下标修改
  • sort: 排序
  • replaceAll: 对所有元素用 UnaryOperator 的返回值替换
{
    List<Integer> l = new ArrayList<>(List.of(1, 2, 3, 4, 5, 4, 3, 2, 1));
    assertEquals(l.get(2), Integer.valueOf(3));
    assertEquals(l.indexOf(3), 2);
    assertEquals(l.indexOf(6), -1);
    assertEquals(l.lastIndexOf(3), 6);
    assertEquals(l.subList(2, 6), List.of(3, 4, 5, 4));
}
{
    List<Integer> l = new ArrayList<>(List.of(1, 2, 3, 4, 5, 4, 3, 2, 1));
    l.set(5, 6);
    assertThat(l, equalTo(List.of(1, 2, 3, 4, 5, 6, 3, 2, 1)));
}
{
    List<Integer> l = new ArrayList<>(List.of(1, 2, 3, 4, 5, 4, 3, 2, 1));
    l.sort(Integer::compareTo);
    assertThat(l, equalTo(List.of(1, 1, 2, 2, 3, 3, 4, 4, 5)));
}
{
    List<Integer> l = new ArrayList<>(List.of(1, 2, 3, 4, 5, 4, 3, 2, 1));
    l.replaceAll(x -> x * x);
    assertThat(l, equalTo(List.of(1, 4, 9, 16, 25, 16, 9, 4, 1)));
}

Set

SetList 的本质区别在于可重复性,Set 中的元素是不可重复的,Set 又分为有序 Set 和无序 Set,有序 Set 中的元素是按顺序排列的,util 中提供了三种实现

  • TreeSet: 有序 Set,元素必须是可比较的,使用红黑树实现,插入删除查找代价都是 O(lgn)
  • HashSet: 无序 Set,元素必须是能被 hash 的,使用 hash 表实现,插入删除查找代价都是 O(1)
  • LinkedHashSet: 无序的 Set,但是提供能插入顺序的遍历,使用 hash + 链表实现,插入删除查找都是 O(1)

Set 没有提供 Collection 接口之外的接口

同时 TreeSet 还实现了 SortedSetNavigableSet

SortedSet 继承自 Set,提供了如下接口:

  • first: 最小的元素
  • last: 最大的元素
  • headSet: 头部集合,小于给定元素的元素构成的集合
  • tailSet: 尾部集合,大于等于给定元素的元素构成的集合
  • subSet: 子集,[from, to) 集合
SortedSet<String> set = IntStream.range(0, 10).boxed().map(x -> "key" + x).collect(Collectors.toCollection(TreeSet::new));
assertEquals(set.first(), "key0");
assertEquals(set.last(), "key9");
assertThat(set.headSet("key3"), equalTo(Set.of("key0", "key1", "key2")));
assertThat(set.tailSet("key7"), equalTo(Set.of("key7", "key8", "key9")));
assertThat(set.subSet("key3", "key7"), equalTo(Set.of("key3", "key4", "key5", "key6")));

NavigableSet 继承自 SortedSet,提供了如下接口:

  • lower: 小于给定值的最大值
  • higher: 大于给定值的最小值
  • floor: 小于等于给定值中的最大值
  • ceiling: 大于等于给定值的最小值
  • pollFirst: 删除并获取最小值
  • pollLast: 删除并获取最大值
  • descendingSet: 获取倒排的集合
  • headSet: 头部集合,提供额外参数是否包含给定值
  • tailSet: 尾部集合,提供额外参数是否包含给定值
  • subSet: 子集,提供额外参数是否包含给定值
{
    NavigableSet<String> set = IntStream.range(0, 10).boxed().map(x -> "key" + x).collect(Collectors.toCollection(TreeSet::new));
    assertEquals(set.lower("key6"), "key5");    // <
    assertEquals(set.higher("key6"), "key7");   // >
    assertEquals(set.floor("key6"), "key6");    // <=
    assertEquals(set.ceiling("key6"), "key6");  // >=
    set.remove("key6");
    assertEquals(set.floor("key6"), "key5");
    assertEquals(set.ceiling("key6"), "key7");
}
{
    NavigableSet<String> set = IntStream.range(0, 5).boxed().map(x -> "key" + x).collect(Collectors.toCollection(TreeSet::new));
    assertEquals(set.pollFirst(), "key0");
    assertThat(set, equalTo(Set.of("key1", "key2", "key3", "key4")));
    assertEquals(set.pollLast(), "key4");
    assertThat(set, equalTo(Set.of("key1", "key2", "key3")));
}
{
    NavigableSet<String> set = IntStream.range(0, 10).boxed().map(x -> "key" + x).collect(Collectors.toCollection(TreeSet::new));
    assertThat(set.descendingSet(), equalTo(Set.of("key9", "key8", "key7", "key6", "key5", "key4", "key3", "key2", "key1", "key0")));
    assertThat(set.headSet("key3", false), equalTo(Set.of("key0", "key1", "key2")));
    assertThat(set.tailSet("key7", true), equalTo(Set.of("key7", "key8", "key9")));
    assertThat(set.subSet("key3", true, "key7", false), equalTo(Set.of("key3", "key4", "key5", "key6")));
}

Queue

Queue 队列(先进先出),继承自 Collection,关注集合的有序性,支持尾部插入,头部删除,以及头部元素的获取,util 提供了三种 Queue

  • LinkedList: LinkedList 实现了 Queue 的接口,元素按插入顺序排列
  • ArrayDeque: 数组实现的 Queue,元素按插入顺序排列
  • PriorityQueue: 优先队列,堆实现,元素按从小到大排列

QueueCollection 基础上提供了如下接口:

  • add: 添加元素,如果队列满了,抛出异常
  • remove: 删除元素,如果队列为空,抛出异常
  • element: 获取头部元素,如果队列为空,抛出异常
  • offer: 添加元素,如果队列满了,返回 false
  • poll: 删除元素,如果队列为空,返回 null
  • peek: 获取头部元素,如果队列为空,返回 null
{
    Queue<Integer> queue = new LinkedList<>();
    // add / remove / element
    assertThrows(NoSuchElementException.class, queue::remove);
    assertThrows(NoSuchElementException.class, queue::element);
    IntStream.range(0, 10).forEach(queue::add);
    assertThat(queue.toArray(), equalTo(new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
    assertEquals(queue.element(), Integer.valueOf(0));
    assertEquals(queue.remove(), Integer.valueOf(0));
}
{
    Queue<Integer> queue = new LinkedList<>();
    // offer / poll / peek
    assertEquals(queue.poll(), null);
    assertEquals(queue.peek(), null);
    IntStream.range(0, 10).forEach(queue::offer);
    assertThat(queue.toArray(), equalTo(new Integer[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}));
    assertEquals(queue.peek(), Integer.valueOf(0));
    assertEquals(queue.poll(), Integer.valueOf(0));
}

Deque

Deque 双端队列,继承自 Queue,关注集合的两端的插入和删除以及两端元素的获取,util 提供了两种 Deque

  • LinkedList: 链表实现的 Deque
  • ArrayDeque: 数组实现的 Deque

DequeQueue 的基础上提供了下面接口:

  • addFirst: 头部插入,队列满,抛异常
  • addLast: 尾部插入,队列满,抛异常
  • removeFirst: 头部删除,队列空,抛异常
  • removeLast: 尾部删除,队列空,抛异常
  • getFirst: 获取头部元素,队列空,抛异常
  • getLast: 获取尾部元素,队列空,抛异常
  • offerFirst: 头部插入,队列满,返回 false
  • offerLast: 尾部插入,队列满,返回 false
  • pollFirst: 头部删除,队列空,返回 null
  • pollLast: 尾部删除,队列空,返回 null
  • peekFirst: 获取头部,队列空,返回 null
  • peekLast: 获取尾部,队列空,返回 null
  • push: 作为 Stack 使用,插入元素(头部插入),队列满,抛异常
  • pop: 作为 Stack 使用,删除元素(头部删除),队列空,抛异常
  • removeFirstOccurrence: 删除第一个与给定值相等的对象,返回是否有元素删除
  • removeLastOccurrence: 删除最后一个与给定值相等的对象,返回是否有元素删除
{
    Deque<Integer> deque = new ArrayDeque<>();
    assertThrows(NoSuchElementException.class, deque::getFirst);
    assertThrows(NoSuchElementException.class, deque::getLast);
    assertThrows(NoSuchElementException.class, deque::removeFirst);
    assertThrows(NoSuchElementException.class, deque::removeLast);
    IntStream.range(0, 5).forEach(deque::addFirst);
    IntStream.range(5, 10).forEach(deque::addLast);
    assertThat(deque.toArray(), equalTo(new Integer[]{4, 3, 2, 1, 0, 5, 6, 7, 8, 9}));
    assertEquals(deque.getFirst(), Integer.valueOf(4));
    assertEquals(deque.getLast(), Integer.valueOf(9));
    assertEquals(deque.removeFirst(), Integer.valueOf(4));
    assertEquals(deque.removeLast(), Integer.valueOf(9));
}
{
    Deque<Integer> deque = new ArrayDeque<>();
    assertEquals(deque.peekFirst(), null);
    assertEquals(deque.peekLast(), null);
    assertEquals(deque.pollFirst(), null);
    assertEquals(deque.pollLast(), null);
    IntStream.range(0, 5).forEach(deque::offerFirst);
    IntStream.range(5, 10).forEach(deque::offerLast);
    assertThat(deque.toArray(), equalTo(new Integer[]{4, 3, 2, 1, 0, 5, 6, 7, 8, 9}));
    assertEquals(deque.peekFirst(), Integer.valueOf(4));
    assertEquals(deque.peekLast(), Integer.valueOf(9));
    assertEquals(deque.pollFirst(), Integer.valueOf(4));
    assertEquals(deque.pollLast(), Integer.valueOf(9));
}
{
    Deque<Integer> deque = new ArrayDeque<>();
    IntStream.range(0, 10).forEach(deque::push);
    assertThat(deque.toArray(), equalTo(new Integer[]{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}));
    assertEquals(deque.element(), Integer.valueOf(9));
    assertEquals(deque.pop(), Integer.valueOf(9));
}
{
    Deque<Integer> deque = new ArrayDeque<>();
    IntStream.range(0, 10).forEach(deque::push);
    assertTrue(deque.removeFirstOccurrence(2));
    assertTrue(deque.removeLastOccurrence(8));
    assertThat(deque.toArray(), equalTo(new Integer[]{9, 7, 6, 5, 4, 3, 1, 0}));
}

Stack

Queue先进先出不同,Stack 是一种代表后进先出的数据结构,util 中并没有提供 Stack 接口,事实上 Deque 中已经包含了 Stack 接口,因此当你需要一个 Stack 的时候,可以构造一个 Deque,java doc 也是这么建议的

此外,util 中还有一个 Stack 类,继承自 Vector,线程安全,这个类的设计和定位比较尴尬,不建议使用

链接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268