Java基础(五)—— 集合

本文最后更新于:2022年7月21日 上午

概览:Java集合

内容不完整,后续补充。

集合框架

  • 位于java.util包中。
  • Collections类中提供了对集合进行排序、遍历等多种算法

要素

  • Collection接口存储一组不唯一、无序的对象、
  • List接口存储一组不唯一、有序的对象。
  • Set接口存储一组唯一、无序的对象。
  • Map接口存储一组键值对象,提供key到value的映射。
  • ArrayList实现了长度可变的数组,在内存中分配连续的空间,遍历元素和随机访问元素的效率比较高。
  • LinkedList采用了链表存储方式,插入、删除元素是效率比较高。
  • HashSet采用了哈希算法实现的SetHashSet底层是采用了
    HashMap实现的,因此查询效率较高,由于采用了hashcode算法直接确定元素的内存地址,增删效率比较高。

ArrayList

  • Arraylist是可以动态增长和缩减的索引序列。
  • 该类封装了一个动态再分配的Object[]数组,每一个类对象都有capacity的属性,当往集合中增加元素时,该属性值会自动增加。
  • ArrayList的用法类似于Vector,但是Vector比较老,缺点很多,不建议使用。
  • ArrayList是线程不安全的,当多个线程访问同一个ArrayList集合时,需要手动保证集合的同步性。而Vector则是线程安全的。

类中的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 版本号
private static final long serialVersionUID = 8683452581122892189L;
// 缺省容量
private static final int DEFAULT_CAPACITY = 10;
// 空对象数组
private static final Object[] EMPTY_ELEMENTDATA = {};
// 缺省空对象数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 元素数组
transient Object[] elementData;
// 实际元素大小,默认为0
private int size;
// 最大数组容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}

方法:

方法 描述
boolean add(Object o) 在列表的末尾顺序添加元素,起始索引位置从0开始。
void add(int index,Object o) 在指定的索引位置添加元素,索引位置介于0和长度之间
int size() 返回列表的元素个数
Object get(int index) 返回指定索引位置处的元素,去除的元素是Object类型,需要强制类型转换
boolean contains(Object o) 判断列表中是否存在指定元素
boolean remove(Object o) 从列表中删除元素
Object remove(int index) 从列表中删除指定位置元素

boolean addAll(Collection c) //讲一个集合中的所有对象添加到此集合中。
void clear() //清空此集合中的所有对象。
boolean equals(Object o) //比较此集合是否与指定对象相等。
boolean isEmpty() //判断此集合是否为空。
Object[] toArray() //姜此集合转换成数组。

应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Cat{
private String name;
private int age;
private boolean sex;

public Cat(){
name = "";
age = 0;
sex= true;
}

public Cat(String name,int age,boolean sex){
this.name = name;
this.age = age;
this.sex = sex;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Cat cat = (Cat) o;
return age == cat.age && sex == cat.sex && Objects.equals(name, cat.name);
}

public void Talk(){
String []talks = {"喵","喵喵","咪","嗷呜","害"};
int ranindex = (int)(Math.random() * 4);
System.out.println(name + " 发出了 " + talks[ranindex]);
}
}

public class TestArrayList {
public static void main(String[] args) {
List<Cat> cats = new ArrayList<>();
cats.add(new Cat("小明",1,true));
cats.add(new Cat("小红",5,true));
cats.add(new Cat("小黄",2,false));
cats.add(2,new Cat("小蓝",4,false));//指定索引插入

System.out.println("Cat nums : " + cats.size());
for(Cat tmp : cats){
tmp.Talk();
}
cats.remove(2);
System.out.println("Cat nums : " + cats.size());
}
}
1
2
3
4
5
6
Cat nums : 4
小明 发出了 喵
小红 发出了 喵
小蓝 发出了 嗷呜
小黄 发出了 嗷呜
Cat nums : 3

Guava Lists

  • Collections.emptyList();可以返回一个空的list
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
List<Integer> intList = Lists.newArrayList(1,2,3,4,5);

// 指定初始化的容量大小
List<String> strList = Lists.newArrayListWithCapacity(5);

for(int i = 0;i<intList.size();i++) {
System.out.println(intList.get(i));
strList.add("[ " + intList.get(i) + " ]");
}

System.out.println("-----------");

// foreach式遍历
for(String str : strList){
System.out.println(str);
}

// 在foreach中删除元素会有异常
// for(String str : strList){
// System.out.println(str);
// strList.remove(str); // ConcurrentModificationException
// }

System.out.println("==========");

// 遍历中删除元素的方式
for(ListIterator<String> itr = strList.listIterator();itr.hasNext();){
String number = itr.next();
System.out.println(number);
itr.remove();
}

System.out.println(strList.size()); // 0

Guava Collections

  • emptyList/emptySet/emptyMap:获取一个空的集合,但是不可写

  • singleton/songletonList:获取只有一个元素的Set/list,但是不可写

  • shuffle打乱一个list

  • sort排序,默认从小到大

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Collections.sort(list,new Comparator<Integer> {
    @override
    public int compare(Integer o1,Integer o2){
    return o1 > o2 ? -1 : (o1 < o2 ? 1 : 0);
    }
    });

    // Guava的方法
    Collections.sort(list,Ordering.natural().reverse());//逆序排序

    // 不打乱原来的排序,生成一个新的排序
    List<Integer> relist = Ordering.natural().reverse().sortedCopy(list);

总结

  • ArrayList可以存放null
  • ArrayList本质上是一个数组,不过是可以自动扩展大小,关键方法在于grow()
  • ArrayList在数据查询方面比较快,而在插入删除方面则比较慢,因为连续的空间,需要移动大量的数据。

LinkedList

  • LinkedList是一个可以在任意位置进行高效插入与删除的有序序列,基于双向链表实现。
  • LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队 列进行操作。
  • LinkedList实现 List 接口,能对它进行队列操作。
  • LinkedList实现 Deque 接口,即能将LinkedList当作双端队列使用。
  • LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
  • LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。 LinkedList 是非同步的。

特殊方法:

方法名 说明
void addFirst(Object o) 在列表首部添加元素
void addLast(Object o) 在列表的末尾添加元素
Object getFirst() 获取列表第一个元素
Object getLast() 获取列表最后一个元素
Object removeFirst() 删除并返回列表第一个元素
Object removeLast() 删除并返回列表最后一个元素

总结

  • 可以存储null
  • 异步,非线程安全
  • 双向链表实现,适用于频繁的插入和删除操作。

Vector & Stack

优先级队列 PriorityQueue

1
2
3
4
5
6
7
8
9
10
11
boolean offer(E e);//将指定元素插入此优先级队列
E peek();//检索,队列为空时返回null
E poll();//检索并删除此队列的头,队列为空时返回null

PriorityQueue(Comparator<? super E> comparator);//指定比较方式
PriorityQueue<int[]> queue = new PriorityQueue<int[]>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1] - o2[1];
}
});

Map

map有两个参数

  • Capacity,默认16
  • Load factor,默认0.75,即表示当当前的容量扩展到最大容量的75%时就会扩大容量一倍,然后重新对已有的内容进行哈希重排,保证o(1)的效率。
  • 默认无序
  • TreeMap是可以排序的
  • Guava Maps.newHashMapWithExpectedSize(size)
    • 指定size之后,会自动计算出对应的初始容量,在达到一定数据量之前不会进行扩容。
  • 线程不安全
  • 线程安全的map:ConcurrentHashMap

Set

基于map来实现

  • Map<E,object>

Guava

●Lists/Sets/ Maps

-提供工厂方法

  • Lists.newArrayList(), 用于创建一个空ArrayList, 与使用原生JDK语法进行比较, 更加简洁,易懂:
  • List<Map<Integer, List>> jdkArrayList = new ArrayList<Map<Integer, List>>();
    List<Map<Integer,List>> guavaArrayList = Lists .newArrayList();
  • Lists. newArrayListWithCapacity(int), 用于创建一个ArrayList, 返回的容器容量和参数给出的大小一致
  • Maps.newHashMapWithExpectedSize(int),用于创建一个HashMap, 返回的容器具有初始容量(不是元素个数),初始容量约为传入参数给出大小的4/3倍。
  • asList/asSet/asMap系列方法,注意,这些方法返回的是视图,即原容器变更会影响这些方法返回的容器内容

集合上的常见操作

  • Lists.reverse用于翻转
  • Lists.transform 用于List变形
1
2
3
4
5
6
7
List<Integer> list = Lists.newArrayList(1,2,3,4,5);
List<String> strList = Lists.transform(list, new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return String.valueOf(integer);
}
});
  • Lists.partition可以用于分割List

  • Sets.filter 过滤元素

    1
    2
    3
    4
    5
    6
    7
    Set<Integer> intSet = Sets.newHashSet(1,2,3,4);
    Set<Integer> greaterTwoSet = Sets.filter(intSet, new Predicate<Integer>() {
    @Override
    public boolean apply(Integer integer) {
    return integer > 2;
    }
    });
  • . Maps. filterKeys/ .filterValues/ . filterEntries,过滤某些关联关系

  • Maps. transformValues/ . transformEntries,变形成另一个Map

  • Maps. difference, 返回两个Map之间的差异

  • 关于Map的补充工厂方法

  • Maps. fromProperties,从Properties文件构造一 个Map, 该Map不可修改

  • Maps. uniquelndex(Iterable, Function), 根据Value构造一个Map, 例如,从数据库中读
    取一批数据,数据的某- -有唯一索引的列建立Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class User{
int id;
String name;
User(int id,String name){
this.id = id;
this.name = name;
}
}

List<User> users = Lists.newArrayList(new User(1,"hi"),new User(2,"world"));

Map<Integer,User> idUserMap = Maps.uniqueIndex(users, new Function<User, Integer>() {
@Override
public Integer apply(User user) {
Preconditions.checkNotNull(user,"用户为空");
return user.id;
}
});

新集合 MultiSet

  • Set中所有元素互不相同。
  • Multiset允许出现重复元素,并且元素之间没有顺序,除了TreeMultiset外。

计数操作

1
2
3
4
5
6
7
8
9
List<Integer> intList = Lists.newArrayList(1,2,3,2,2,2,3,1,4,5);
Multiset<Integer> multiset = HashMultiset.create(intList);
for(Integer i : multiset.elementSet()){
System.out.println(i + " in set counts: " + multiset.count(i));
}

for(Multiset.Entry<Integer> entry : multiset.entrySet()){
System.out.println(entry.getElement() + " in set counts: " + entry.getCount());
}

MultiMap

  • 可以建立1-> n的关系
  • 原生的1对多的建立方式是,key对应一个set集合

Immutable

●ImmutableXXX
. ImmutableXXX
提供不可修改容器的功能,保证返回的容器不能被调用者修改,并且原容器的修改不会影响ImmutableXXX

对不可靠的客户代码库来说,它使用安全,可以在未受信任的类库中安全的使用这些对象

线程安全的: Immutable对象在多 线程下安全,没有竞态条件不需要支持可变性,可以尽量节省空间和时间的开销。所有的不可变集合实现都比可变集合更加有效的利用内存
可以被使用为一个常量,并且期望在未来也是保持不变的

  • 不可变指的是不能修改引用的指向,不能add内容,是容器内的元素还是可以继续修改的。
  • 返回的对象不是原容器的视图,而是原容器的一种拷贝。
    • 不是deep copy,所以还是容器出现问题
  • 但是JDK提供的类似容器返回的是试图

BiMap

●BiMap

  • JDK提供的Map只提供根据Key查找Value,无法通过Value查找Key
    .有些场景,需要根据Value查找Key, 例如:
    .根据城市Code查询城市名称,同时还需要根据名称查询Code
    .固定集合内单词的中英文互译
    . Guava提供BiMap,使得Key-Value均可以查询
  • BiMap.get- -根据Key查询Value
  • BiMap.inverse().get– 转换Key-Value顺序, 再通过get根据Value查询Key
    .需要注意的是,使用BiMap时,Key-Value必须都是唯一 的,即所有的Value也必须互不
    相同

RangeSet/ RangeMap
-用于存放区间(Range) 的容器,并提供诸如区间合并,区间分裂等功能

●AtomicLongMap
-能对Map中的Value进行原子的更新
-常用来进行统计,例如,监控系统可以使用AtomicLongMap记录各项监控数据等
-每一个Key都是一个监控的名称,Value都是监控值

  • addAndGet()/ getAndAdd()- - - 增加然后获取值/获取完值之后增加
  • incrementAndGet()/ decrementAndGet()/ getAndIncrement()/ getAndDecrement()
    . ++i/–i/i++/i–
  • removeAllZeroes()- - - 去除所有为0的entry, 非原子,即此方法调用过程中,其他线程可能
    取到Map中的0值
  • sum()- –累积值

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!