八、集合
1.概述
- 集合、数组都是对多个数据进行存储操作的结构,简称java容器【此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储】。
- 数组在内存存储方面的特点:
- 数组初始化以后,长度就确定了。
- 数组声明的类型,就决定了进行元素初始化时的类型。
- 数组在内存存储方面的缺点:
- 数组初始化以后,长度就不可变了,不便于扩展。
- 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。同时无法直接获取存储元素的个数。
- 数组存储的数据是有序的、可以重复的。存储数据的特点单一。
- Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组。
2.java集合框架
- Java 集合可分为 Collection 和Map两种体系。
- Collection接口:单列数据,定义了存取一组对象的方法的集合【存储一个一个的对象】。
- List: 元素有序、可重复的集合。
- ArrayList、LinkedList、Vector
- Set: 元素无序、不可重复的集合。
- HashSet、LinkedHashSet、TreeSet
- ......
- List: 元素有序、可重复的集合。
- Map接口: 双列数据,保存具有映射关系“key-value对”的集合【存储一对一对的数据】。
- HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
3.Collection
Collection接口
- Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法既可用于操作Set集合,也可用于操作List和Queue集合。
- JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List)实现。
- 在 Java5 之前,Java集合会丢失容器中所有对象的数据类型,把所有对象都当成Object类型处理;从JDK5.0增加了泛型以后, Java 集合可以记住容器中对象的数据类型。
Collection 接口方法
- 1.添加
- add(Object obj)
- addAll(Collection coll)
- 2.获取有效元素的个数
- int size()
- 3.清空集合
- void clear()
- 4.是否是空集合
- boolean isEmpty()
package com.yao.practice2;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import org.junit.Test;
public class CollectionTest {
@Test
public void test1() {
Collection coll = new ArrayList();
// add(Object e):将元素e添加到集合coll中
coll.add("a");
coll.add(new String("b"));
coll.add(1);// 自动装箱
coll.add(new Date());
System.out.println(coll);
// size():获取添加的元素个数
System.out.println(coll.size());
// addAll():将一个集合中的元素加入到另一个集合中
coll.addAll(coll);
System.out.println(coll);
// clear():清空集合元素
coll.clear();
// isEmpty():判断当前集合是否为空
System.out.println(coll.isEmpty());
}
}
程序运行结果:
[a, b, 1, Tue Dec 03 15:48:50 CST 2019]
4
[a, b, 1, Tue Dec 03 15:48:50 CST 2019, a, b, 1, Tue Dec 03 15:48:50 CST 2019]
true
-
5.是否包含某个元素
- boolean contains(Objectobj):是通过元素的equals方法来判断是否是同一个对象
- boolean containsAll(Collectionc):也是调用元素的equals方法来比较的。 拿两个集合的元素挨个比较。
package com.yao.practice2; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import org.junit.Test; class User { private String name; private int age; public User() { super(); } public User(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User [name=" + name + ", age=" + age + "]"; } } public class CollectionTest { @Test public void test2() { Collection coll2 = new ArrayList(); coll2.add("a"); coll2.add(123); coll2.add(new String("b")); coll2.add(true); coll2.add(new User("haha", 18)); // contains():判断当前集合中是否包含obj System.out.println(coll2.contains(123));// true System.out.println(coll2.contains(new String("a")));// true,调用equals,String中重写了equals方法,比较内容 System.out.println(coll2.contains(new User("haha", 18)));// false,User类中没有重写equals,比较引用 //containsAll(Collection coll2):判断coll2中的所有元素是否都存在于当前集合中 Collection coll = Arrays.asList(123,"a");//true System.out.println(coll2.containsAll(coll));//true } }package com.yao.practice2; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import org.junit.Test; class User { private String name; private int age; public User() { super(); } public User(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User [name=" + name + ", age=" + age + "]"; } //重写equals() @Override public boolean equals(Object obj) { System.out.println("调用重写的equals方法"); if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; User other = (User) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } public class CollectionTest { @Test public void test1() { Collection coll = new ArrayList(); // add(Object e):将元素e添加到集合coll中 coll.add("a"); coll.add(new String("b")); coll.add(1);// 自动装箱 coll.add(new Date()); System.out.println(coll); // size():获取添加的元素个数 System.out.println(coll.size()); // addAll():将一个集合中的元素加入到另一个集合中 coll.addAll(coll); System.out.println(coll); // clear():清空集合元素 coll.clear(); // isEmpty():判断当前集合是否为空 System.out.println(coll.isEmpty()); } @Test public void test2() { Collection coll2 = new ArrayList(); coll2.add("a"); coll2.add(123); coll2.add(new String("b")); coll2.add(true); coll2.add(new User("haha", 18)); // contains():判断当前集合中是否包含obj // 在判断时,会调用obj对象所在类的equals()。 System.out.println(coll2.contains(123));// true System.out.println(coll2.contains(new String("a")));// true System.out.println(coll2.contains(new User("haha", 18)));// true } } 运行结果: true true 调用重写的equals方法 调用重写的equals方法 调用重写的equals方法 调用重写的equals方法 调用重写的equals方法 true
@向Collection接口的实现类的对象中添加数据obj时,要去obj所在类重写equals()。
-
6.删除
- boolean remove(Objectobj):通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素。
- boolean removeAll(Collectioncoll):取当前集合的差集。
@Test public void test3() { Collection coll3 = new ArrayList(); coll3.add("a"); coll3.add(123); coll3.add(new String("b")); coll3.add(true); coll3.add(new User("haha", 18)); //remove(Object obj):从当前集合中移除obj元素 coll3.remove(123); coll3.remove(new User("haha", 18)); System.out.println(coll3); //removeAll(Collection coll):从当前集合中移除coll中所有的元素 Collection coll = Arrays.asList(123,"a"); coll3.removeAll(coll); }-
程序test3中
System.out.println(coll3);的部分实现源码;//【1】打印集合coll3元素 System.out.println(coll3); //【2】调用println方法 public void println(Object x) { String s = String.valueOf(x); synchronized (this) { print(s); newLine(); } } //【3】调用静态方法valueOf public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); } //调用Object类方法toString,但是toString方法重写过了 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } //【4】调用重写方法toString @Override public String toString() { return "User [name=" + name + ", age=" + age + "]"; }
-
7.取两个集合的交集
- boolean retainAll(Collection c):把交集的结果存在当前集合中,不影响c。
@Test public void test4() { Collection coll3 = new ArrayList(); coll3.add("a"); coll3.add(123); coll3.add(new String("b")); coll3.add(true); coll3.add(new User("haha", 18)); //retainAll(Collection coll):交集:获取当前集合和coll集合的交集 Collection coll = Arrays.asList(123,"a"); coll3.retainAll(coll); System.out.println(coll3); } -
8.集合是否相等【与obj类型有关,List有序,Set无序】
- boolean equals(Object obj)
-
9.Arrays.asList的用法
-
使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportOperationException异常。
-
asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。
-
用asList方法产生的List是固定大小的,这也就意味着任何改变其大小的操作都是不允许的。asList方法返回的确实是一个 ArrayList,但这个 ArrayList并不是java.util.ArrayList,而是 java.util.Arrays的一个内部类。这个内部类用一个final 数组来保存元素,因此用asList方法产生的ArrayList是不可修改大小的。
//部分源码 public class Arrays { 【1】 public static <T> List<T> asList(T... a) { return new ArrayList<>(a); } 【2】ArrayList<E>是一个内部类 private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable{ private static final long serialVersionUID = -2764017481108945198L; private final E[] a; ArrayList(E[] array) { a = Objects.requireNonNull(array); } } 【3】requireNonNull public static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; } } -
创建一个真正的 ArrayList:
List<String> list =new ArrayList<>(Arrays.asList(s)); -
对list或原数组操作都会影响另一个
public void test5() { String[] s = new String[] {"a","b","c"}; for(String temp:s) { System.out.print(temp); } System.out.println(); //数组--->集合:调用Arrays类的静态方法asList() List<String> list = Arrays.asList(s); //list.add("d");//抛出UnsupportOperationException异常。 System.out.println(list); s[0]="w"; System.out.println(list); list.set(2, "d"); for(String temp:s) { System.out.print(temp); } } 运行结果: abc [a, b, c] [w, b, c] wbd -
不支持基本数据类型数据的数组
public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }- 可以看到:参数类型是 T ,根据官方文档的描述,T 是数组元素的 class。如果你对反射技术比较了解的话,那么 class 的含义想必是不言自明。我们知道任何类型的对象都有一个class属性,这个属性代表了这个类型本身。基本数据类型,比如 int,short,long等,是没有这个属性的,具有class属性的是它们所对应的包装类Integer,Short,Long。参考:Arrays.asList使用指南。
- 因此,这个错误产生的原因可解释为:asList方法的参数必须是对象或者对象数组,而基本数据类型不是对象——这也正是包装类出现的一个主要原因。当传入一个基本数据类型数组时,asList的真正得到的参数就不是数组中的元素,而是数组对象本身!此时List 的唯一元素就是这个数组。
- 解决方案:使用包装类数组
@Test public void test5() { //数组--->集合:调用Arrays类的静态方法asList() List<String> list = Arrays.asList(new String[] {"a","b","c"}); System.out.println(list); List<int[]> arr1 = Arrays.asList(new int[] {123,456}); //List arr1 = Arrays.asList(new int[] {123,456}); System.out.println(arr1); List<Integer> arr2 = Arrays.asList(new Integer[] {123,456}); //List arr2 = Arrays.asList(new Integer[] {123,456}); System.out.println(arr2); List arr3 = Arrays.asList(123,456); System.out.println(arr3); } 运行结果: [a, b, c] [[I@4501b7af] [123, 456] [123, 456]
-
-
10.获取集合对象的哈希值
- hashCode()
-
11.集合元素的遍历,使用Iterator接口
- iterator(): 返回迭代器对象,用于集合遍历
Iterator迭代器接口
- Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
- GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。 迭代器模式,就是为容器而生。
- Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。
- Iterator仅用于遍历集合,Iterator本身并不提供承装对象的能力。如果需要创建Iterator对象,则必须有一个被迭代的集合。
- 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。
- code
package com.yao.practice2;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import org.junit.Test;
public class ItetatorTest {
@Test
public void test() {
Collection coll = new ArrayList();
coll.add("a");
coll.add(123);
coll.add(new String("b"));
coll.add(true);
coll.add(new User("haha", 18));
Iterator iterator = coll.iterator();
// 方式一:不推荐
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// // 报异常:NoSuchElementException
// System.out.println(iterator.next());
// 方式二:不推荐
// for(int i=0;i<coll.size();i++) {
// System.out.println(iterator.next());
// }
// 方式三:推荐
// hasNext():判断是否还有下一个元素
while (iterator.hasNext()) {
// next():①指针下移②将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
System.out.println("***************************");
// foreach遍历
// for(集合中元素的类型 局部变量:集合对象)
// 内部仍然调用了迭代器
//【注意】修改obj不会更改coll的值
for (Object obj : coll) {
System.out.println(obj);
}
System.out.println("***************************");
// Iterator接口remove():
iterator = coll.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
if ("b".equals(obj)) {
iterator.remove();
}
}
iterator = coll.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
运行结果:
a
123
b
true
User [name=haha, age=18]
***************************
a
123
b
true
User [name=haha, age=18]
***************************
a
123
true
User [name=haha, age=18]
4.Collection子接口之一:List接口
List接口概述
public interface List<E> extends Collection<E>{...}- 鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组【理解为“动态数组”】。
- List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
- JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector。
- ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储。
- LinkedList:对于频繁的插入、删除操作,使用此类效率比Arraylist高;底层使用双向链表存储。
- Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用0bject[] elementData存储。
List接口方法
-
List除了从Collection集合继承的方法外,List集合里添加了一些根据索引来操作集合元素的方法。
- void add(int index,Objectele):在index位置插入ele元素
- boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
- Object get(int index):获取指定index位置的元素
- int indexOf(Object obj):返回obj在集合中首次出现的位置
- int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
- Object remove(int index):移除指定index位置的元素,并返回此元素
- Object set(int index, Object ele):设置指定index位置的元素为ele
- List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
-
区分List中remove(int index) 和remove(0bject obj)
public class ListExer { public void testListRemove() { List list = new ArrayList(); list.add(1);| list.add(2); list.add(3); updateList(list); System.out.print1n(list); } private void updateList(List list) { //list.remove(2); list.remove(new Integer(2)); } }
List实现类之一: ArrayList
- ArrayList是List接口的典型实现类、主要实现类
- 本质上,ArrayList是对象引用的一个”变长”数组
- ArrayList源码
- JDK7
ArrayList list = new ArrayList();//底层创建了长度是10的object[]数组eLementData List.add(123);//eLementData[0] = new Integer(123); ... List.add(11);//如果此次的添加导致底层eLementData数组容量不够,则扩容。 默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。 结论:建议开发中使用带参的构造器: ArrayList list = new ArrayList(int capacity)- JDK8
ArrayList List = new ArrayList();//底层object[] elementData初始化为{}.并没有创建长度为10的数组 list. add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到eLementData[0] ... 后续的添加和扩容操作与jdk7无异。- 小结 jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。
List实现类之二: LinkedList
- 对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高
- 新增方法:
- void addFirst(Object obj)
- void addLast(Object obj)
- Object getFirst()
- Object getLast()
- Object removeFirst()
- Object removeLast()
- 源码分析
LinkedList list = new LinkedList();内部声明了Node类型的first和Last属性,默认值为null list.add(123);//将123封装到Node中,创建了Node对象 其中,Node定义为:双向链表 private static class Node<E> { E item;| Node<E> next; Node<E> prev; Node (Node<E> prev,E eLement,Node<E> next) { this.item = eLement; this.next = next; this.prev = prev; } }
List 实现类之三: Vector
Vector的源码分析: jdk7和jdk8 中通过Vector()构造器创建对象时,底层都创建了长度为10的数。在扩容方面,默认扩容为原来的数组长度的2倍。
5.Collection子接口之二:Set接口
Set 接口概述
- Set接口是Collection的子接口:
public interface Set<E> extends Collection<E>{...},Set接口没有提供额外的方法。 - Set集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set集合中,则添加操作失败。
- Set判断两个对象是否相同不是使用==运算符,而是根据equals()方法。
- Set接口:存储无序的、不可重复的数据。-->数学中的“集合”。
- HashSet:作为Set接口的主要实现类;线程不安全的;可以存储nulL值。
- LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历。
- TreeSet:可以按照添加对象的指定属性,进行排序。
- Set的无序性与不可重复性的理解(HashSet为例)
无序性:存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
不可重复性:保证添加的元素按照equals()判断时,不能返回true。即相同的元素只能添加一个。
Set实现类之一: HashSet
- HashSet是Set接口的典型实现,大多数时候使用Set集合时都使用这个实现类。
- HashSet按Hash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
- HashSet具有以下特点:
- 不能保证元素的排列顺序
- HashSet不是线程安全的
- 集合元素可以是null
- HashSet集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相等,并且两个对象的equals()方法返回值也相等。
- 对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码” 。
- 向HashSet中添加元素的过程:【HashSet底层:数组+链表】
- 当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据hashCode值,通过某种散列函数决定该对象在HashSet底层数组中的存储位置。(这个散列函数会与底层数组的长度相计算得到在数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布,该散列函数设计的越好)。
- 如果此位置上没有其它元素,则添加成功。
- 如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了,那么会通过链表的方式继续链接。
- JDK7中:新添加元素放到数组中,指向原来的元素
- JDK8中:原来的元素指向新添加的元素【七上八下】
- 为什么用Eclipse/IDEA复写hashCode方法,有31这个数字?
- 选择系数的时候要选择尽量大的系数。因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。(减少冲突)
- 并且31只占用5bits,相乘造成数据溢出的概率较小。
- 31可以由i*31==(i<<5)-1来表示,现在很多虚拟机里面都有做相关优化。(提高算法效率)
- 31是一个素数,素数作用就是如果我用一个数字来乘以这个素数,那么最终出来的结果只能被素数本身和被乘数还有1来整除!(减少冲突)
- 重写hashCode()方法的基本原则
- 在程序运行时,同一个对象多次调用hashCode()方法应该返回相同的值。
- 当两个对象的equals()方法比较返回true时,这两个对象的hashCode()方法的返回值也应相等。
- 对象中用作equals()方法比较的Field,都应该用来计算hashCode值。
- 重写equals()方法的基本原则
- 重写equals方法的时候一般都需要同时重写hashCode方法。通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。
Set实现类之二:LinkedHashSet
- LinkedHashSet是HashSet的子类:
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>。 - LinkedHashSet根据元素的hashCode值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
- LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里的全部元素时将有很好的性能,因为它以链表来维护内部顺序。
- LinkedHashSet不允许集合元素重复。
Set实现类之三:TreeSet
- TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。
- 向TreeSet中添加的数据,要求是相同类的对象。
- TreeSet底层使用红黑树结构存储数据。
- TreeSet两种排序方法:自然排序和定制排序。默认情况下,TreeSet采用自然排序。
- 自然排序(实现Comparable接口)
- 对于TreeSet集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj)方法比较返回值。
- TreeSet的自然排序要求元素所属的类实现Comparable接口。
- TreeSet会调用集合元素的compareTo(Object odj)方法方法来比较元素之间的大小关系,然后将集合元素按升序(默认情况)排列。
- 向TreeSet中添加元素时,只有第一个元素无须比较compareTo()方法,后面添加的所有元素都会调用compareTo()方法进行比较。
- 因为只有相同类的两个实例才会比较大小,所以向 TreeSet中添加的应该是同一个类的对象。
- 当需要把一个对象放入TreeSet中,重写该对象对应的 equals()方法时,应保证该方法与compareTo(Object obj)方法有一致的结果:如果两个对象通过equals() 方法比较返回true,则通过compareTo(Object obj) 方法比较应返回 0。否则,让人难以理解。
- 定制排序
- 定制排序中,比较两个对象是否相同的标准为: compare()返回0。不再是equals()。
- TreeSet的自然排序要求元素所属的类实现Comparable接口,如果元素所属的类没有实现Comparable接口,或不希望按照升序(默认情况)的方式排列元素或希望按照其它属性大小进行排序,则考虑使用定制排序。定制排序,通过Comparator接口来实现。需要重写compare(T o1,T o2)方法。
- 要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构造器。
- 此时,仍然只能向TreeSet中添加类型相同的对象。否则发生ClassCastException异常。
4.Map接口
Map接口概述
- Map与Collection并列存在。用于保存具有映射关系的数据:key-value。
- Map中的key和value都可以是任何引用类型的数据。
- Map中的key用Set来存放,不允许重复,即同一个Map对象所对应的类,须重写hashCode()和equals()方法。
- key和value之间存在单向一对一关系,即通过指定的key总能找到唯一的、确定的value。
- Map接口的常用实现类:HashMap、TreeMap、LinkedHashMap和Properties。其中,HashMap是Map接口使用频率最高的实现类。
Map实现类之一:HashMap
- HashMap是Map接口使用频率最高的实现类。
- 允许使用null键和null值,与HashSet一样,不保证映射的顺序。
- 所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写:equals()和hashCode()。
- 所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类要重写:equals()。
- 一个key-value构成一个entry。
- 所有的entry构成的集合是Set:无序的、不可重复的。
- HashMap判断两个key相等的标准是:两个key通过equals()方法返回true,hashCode值也相等。
- HashMap判断两个value相等的标准是:两个value通过equals()方法返回true。
Map实现类之二: LinkedHashMap
- LinkedHashMap是HashMap的子类:
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>。 - 在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序。
- 与LinkedHashSet类似,LinkedHashMap可以维护Map的迭代顺序:迭代顺序与Key-Value对的插入顺序一致。
Map实现类之三:TreeMap
- TreeMap存储Key-Value对时,需要根据key-value对进行排序。TreeMap可以保证所有的Key-Value对处于有序状态。
- TreeSet底层使用红黑树结构存储数据。
- TreeMap的Key的排序:
- 自然排序:TreeMap的所有的Key必须实现Comparable接口,而且所有的Key应该是同一个类的对象,否则将会抛出 ClasssCastException。
- 定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。此时不需要Map的Key实现Comparable接口。
- TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0。
Map实现类之四: Hashtable
- Hashtable是个古老的Map实现类,JDK1.0就提供了。不同于HashMap,Hashtable是线程安全的。
- Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询速度快,很多情况下可以互用。
- 与HashMap不同,Hashtable不允许使用null作为key和value。
- 与HashMap一样,Hashtable也不能保证其中Key-Value对的顺序。
- Hashtable判断两个key相等、两个value相等的标准,与HashMap一致。
Map实现类之五: Properties
- Properties 类是Hashtable的子类,该对象用于处理属性文件。
- 由于属性文件里的key、value都是字符串类型,所以Properties里的key和value都是字符串类型。
- 存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法。
Collections工具类
概述
- Collections是一个操作Set、List和Map等集合的工具类。
- Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。
- 排序操作:(均为static方法)
- reverse(List):反转List中元素的顺序
- shuffle(List):对List集合元素进行随机排序
- sort(List):根据元素的自然顺序对指定List集合元素按升序排序
- sort(List,Comparator):根据指定的Comparator 产生的顺序对List集合元素进行排序
- swap(List,int,int):将指定list集合中的i处元素和j处元素进行交换
Collections常用方法
- Object max(Collection): 根据元素的自然顺序,返回给定集合中的最大元素
- Object max(Collection, Comparator): 根据 Comparator 指定的顺序,返回 给定集合中的最大元素
- Object min(Collection)
- Object min(Collection,Comparator)
- int frequency(Collection,Object): 返回指定集合中指定元素的出现次数
- void copy(Listdest,Listsrc):将src中的内容复制到dest中
- boolean replaceAll(List list,Object oldVal, Object newVal):使用新值替换List 对象的所有旧值
Collections常用方法:同步控制
- Collections类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。