一些之前不太了解的东西
一、概述
- 集合是对象的容器,能够填充和存储不同类型的对象,也能够通过范式规范存储对象的类型,存入相同类型的对象。集合能够自动扩充,是不定长的“数组”。这些容器具有几个特征:存储数据的特征(单列/键值对)、对象的类型(范式)、容器的数据结构(数组、链表、红黑树)、索引、数据的不重复性、有序性、非空性和安全性。Java针对数据的批量存储提供了Collection和Map两类容器,分别存储单列数据和键值对数据。两类容器都有对应的不同存储数据类型的实现类和接口。不同集合对象的主要的特点详见下面的思维导图。(ᴵ上标表示是一个接口)
集合工具类:位于java.util.Arrays(操作数组对象的工具类)、java.util.Collections(操作集合对象的工具类)中,其中都是静态方法。
Collections.fill(list, 20); // 将list中存在的元素替换为20
Collections.max(list); // 返回list最大值
Collections.min(list); // 返回list最大值
Collections.reverse(list); // 反转list
Collections.sort(list); // 将list按自然顺序排序
Collections.shuffle(list); // 打乱list顺序
Collections.addAll(list, 1, 3, 5, 7); // 向list中批量添加数据
二、Collection集合
Collection是实现单列数据的接口,子接口包括List和Set。List的实现类包括ArrayList和LinkedList;Set的实现类包括HashSet和TreeSet。
Collection是迭代器Iterable的实现接口。每个Collection的实现类都有一个迭代器对象,能够像指针一样指向下一个对象。可以通过循环和迭代器遍历数据。使用迭代器时不可增删数据。
Iterator iterator = arraylist.iterator();
while(iterator.hasNext()) {
Object obj = iterator.next();
System.out.println(obj);
}
HashSet是Set接口的实现类,同时继承了HashMap(详见哈希们)。TreeSet是Set接口的子接口SortedSet的实现类,同时继承了TreeMap,将数据存入Key中。
三、Map集合
Map是实现键值对(Key-Value)的接口,能够存储映射数据,其实现类包括HashMap和TreeMap。
实现HashMap、HashSet中对象的不重复性:比较两个对象是否相同,是根据数据的哈希值和equals()方法进行判断的:首先调用类中的hashCode()方法,若为true,再调用equals()(默认判断地址),若都返回true则判断该数据与其内的某一数据相同。可以重写这两个方法以判断一个数据是否可以加入HashMap或HashSet中。当加入是数据Key值相同而Value值不同时,会更新Key值对应的Value值。
- hashCode()方法:在Object的hashCode()方法是根据对象在内存中的特征返回一个哈希值,可以重写这个方法,返回通过Objects.hash()静态方法来生成对象数据的哈希值,而不是地址的哈希值。
- equals()方法:在Object的equals()方法是比较地址,当然这有很多局限性。故可以对这个方法进行重写,以比较内部属性是否相等,而不是仅比较地址。很多包装类都对equals()进行了重写,以比较包装的内容。
- 对这个方法重写时,需要满足几个约定俗成的条件:自反性、对称性、传递性、一致性、非空性。相等的一般依据是:两个对象同类、两个同时为空或同时不为空、对应属性值相等。
- List集合类型是可以存入重复数据的,故不需要判断存入的数据是否相同。而TreeMap和TreeSet则通过 comparaTo()方法或比较器来判断是否重复。
五、有序性和元素可比较性
集合对象的有序性:集合类型在内存中的顺序主要有三种:按存储顺序、按哈希值、自然排序(定制化排序)。
- List接口的实现类ArrayList、LinkedList,以及LinkedHashMap是按存储顺序排序。
- HashMap和HashSet是按哈希值存储,故表现出数据是“无序”的。
- TreeMap和TreeSet调用了java.lang.Comparable接口的comparaTo()方法,或实现比较器java.util.Comparator接口来比较存入的对象,并放入红黑树中。当存入的对象重写了comparaTo()方法,便可以比较这些对象,并放入红黑树中。大部分包装类(字符串、基本数据类型的包装类)都默认重写了comparaTo()方法,对对象进行合适地比较,呈现出“自然”的顺序。当对象不具有可比性时,便不能存入其中。
实现TreeMap、TreeSet中对象的可比较性:为了实现普通对象的可比较性,需要通过对对象的某些特征进行比较。
comparaTo()方法:自然排序实现的方法。该方法放在要放入集合的对象的类中,然后该类的对象便是可比较的。该方法返回值的正负,决定了要存入的数据是放在比较节点的前面还是后面。
public class Student implements Comparable { // 实现接口
int age;
@Override // 重写方法
public int compareTo(Object o) { // o是要存入的对象
Student other = (Student)o; // 将对象转型,用other表示要存入的对象,用this表示当前比较的节点
if(this.age > other.age) { // 要传入的对象较小时
return 1; // 正数,放在比较节点的后面
} else if(this.age < other.age) { // 要传入的对象较大时
return -1; // 负数,放在比较节点的前面
} else {
return 0; // 表示两个对象相同(对于TreeMap,将修改对应的Value值为新值;对于TreeSet,该数据不放入)
}
} // 大的放前面,小的放后面,降序
}
比较器排序:又称客户化排序。该方法写在一个实现类(一般使用匿名内部类实现)中,并将实现类对象传入集合类型构造器即可实现排序。同样是返回一个正负值。
Comparator c = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Student s1 = (Student) o1;
Student s2 = (Student) o2;
return s1.age > s2.age ? 1 : (s1.age == s2.age ? 0 : -1);
// 或者 return s1.age - s2.age;
}
};
Set set = new TreeSet(c);
六、其它
容器对象的非空性:HashTable是非空的集合。
- 容器对象的安全性:Vector和Hashtable是线程安全的集合,其中的大部分方法具有synchronized修饰符。
- 容器对象可以存储任意的引用类型数据。若要对存入的类型进行规范,则需要使用范式,详见范型。