赞
踩
目录
集合、数组都是对多个数据进行存储操作的结构,简称Java容器
说明 : 此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)
数组在存储多个数据方面的特点 :
一旦初始化之后,其长度就确定了
数组一旦定义好,其元素的类型也就确定了,只能操作指定类型的数据。比如 : String[] arr,int[] arr;
数组在存储多个数据方面的缺点 :
一旦初始化之后,其长度就不可修改
数组中提供的方法非常有限,对于 : 添加、删除、插入数据等操作,非常不方便,同时效率不高
获取数组中实际元素的个数的需求,数组中没有现成的属性或方法可用
数组存储数据的特点是有序的,可以存储重复的数据(有序,可重复),对于无序,不可重复的需求不能满足
关于数组的优缺点,集合都能很好的满足,其使用场景有 : 数据库返回的为一个List
Java集合可分为Collection和Map两种体系 :
Collection接口 : 单列数据,定义了存取一组对象的方法的集合
List : 元素有序、可重复的集合
Set : 元素无序、不可重复的集合
Map接口 : 双列数据,保存具有映射关系 "key - value 对" 的集合
Collection接口继承树 :
说明 : 实线为继承,虚线为实现
Map接口继承树 :
- |----Collection接口 : 单列集合,用来存储一个一个的对象
- |----List接口 : 存储有序、可重复的数据 --> "动态"数组
- |----ArrayList、LinkedList、Vector
-
- |----Set接口 : 存储无序、不可重复的数据 --> 高中讲的 "集合"
- |----HashSet、LinkedHashSet、TreeSet
-
-
- |----Map接口 : 双列集合,用来存储一对 (key - value) 一对的数据 --> 高中函数 : y = f(x)
- |----HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
最基本的15种方法 :
- add(Object obj)、addAll(Collection coll)、size()、isEmpty()、clear();
-
- contains(Object obj)、containsAll(Collection coll)、remove(Object obj)、removeAll(Collection voll)、retainsAll(Collection coll)、equals(Object obj);
-
- hashCode()、toArray()、iterator();
1、添加
add(Object obj)
addAll(Collection coll)
2、获取有效元素的个数
int size()
3、清空集合
void clear()
4、是否是空集合
boolean isEmpty()
- /*
- Collection接口中声明的方法测试
- */
- @SuppressWarnings("rawtypes")
- @Test
- public void test01(){
- Collection coll = new ArrayList();
-
- //add(Object e) : 将元素e添加到集合coll中
- coll.add("AA");
- coll.add(123);
- coll.add(new Object());
- coll.add("BB");
-
- //size() : 获取添加元素的个数
- System.out.println(coll.size());//4
-
- Collection coll1 = new ArrayList();
- coll1.add("AA");
- coll1.add(new Date());
-
- //addAll(Collection c) : 将c集合中的元素全部添加到当前集合中
- coll.addAll(coll1);
- System.out.println(coll.size());//6
-
- System.out.println(coll);
- //[AA, 123, java.lang.Object@76fb509a, BB, AA, Fri Aug 12 17:38:17 CST 2022]
-
- //clear() : 清空集合元素
- coll.clear();
-
- //isEmpty() : 判断当前集合是否为空
- System.out.println(coll.isEmpty());//true
- }
5、是否包含某个元素
boolean contains(Object obj):是通过元素的equals方法来判断是否是同一个对象
boolean containsAll(Collection c):也是调用元素的equals方法来比较的。拿两个集合的元素挨个比较。
- /*
- Collection接口中声明的方法测试
- 向Collection接口的实现类中添加数据obj时,要求obj所在的类要重写equals()方法
- */
- @SuppressWarnings("rawtypes")
- @Test
- public void test02(){
- Collection coll = new ArrayList();
-
- coll.add(123);
- coll.add(456);
- coll.add(new String("Tom"));
- coll.add(false);
- Person p = new Person("Jerry", 20);
- coll.add(p);
-
- //contains(Object obj) : 判断当前集合是否包含obj
- //我们在判断时会调用obj对象所在类的equals()方法
- boolean contains = coll.contains(123);
- System.out.println(contains);//true
- System.out.println(coll.contains(new String("Tom")));//true
- // --> 判断的内容(重写的equals方法)
- System.out.println(coll.contains(new Person("Jerry", 20)));//false
- // --> 由于equals方法未重写 所以就是判断的地址
-
- //containsAll(Collection coll) : 判断形参coll中的所有元素是否都存在于当前集合中
- List list = Arrays.asList(123, 456);//返回list类型 list是collection的子接口
- System.out.println(coll.containsAll(list));//true
- }
6、删除
boolean remove(Object obj) :通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素
boolean removeAll(Collection coll):取当前集合的差集
- /*
- Collection接口中声明的方法测试
- */
- @SuppressWarnings("rawtypes")
- @Test
- public void test03(){
- Collection coll = new ArrayList();
-
- coll.add(123);
- coll.add(456);
- coll.add(new String("Tom"));
- coll.add(false);
- coll.add(new Person("Jerry", 20));
-
- //remove(Object obj) : 从当前集合中移除
- System.out.println(coll.remove(123));//true
- System.out.println(coll);//[456, Tom, false, Person{name='Jerry', age=20}]
-
- System.out.println(coll.remove(new Person("Jerry", 20)));
- System.out.println(coll);
-
- //removeAll(Collection coll) : 从当前集合中移除coll当中的所有元素 (差集)
- List list = Arrays.asList(123, 456);
- System.out.println(coll.removeAll(list));//true
- }
7、取两个集合的交集
boolean retainAll(Collection c):把交集的结果存在当前集合中,不影响c
8、集合是否相等
boolean equals(Object obj)
- /*
- Collection接口中声明的方法测试
- */
- @SuppressWarnings("rawtypes")
- @Test
- public void test04(){
- Collection coll = new ArrayList();
-
- coll.add(123);
- coll.add(456);
- coll.add(new String("Tom"));
- coll.add(false);
- coll.add(new Person("Jerry", 20));
-
- //retainAll(Collection coll) : 获取当前集合和coll集合之间的交集 修改当前集合的值为交集的值
- // List list = Arrays.asList(123, 456, 789);
- // System.out.println(coll.retainAll(list));//true
- // System.out.println(coll);//[123, 456]
-
- //equals(Object obj) : 判断当前集合中的每个元素和另一个集合是否相等(顺序元素都相等)
- Collection coll1 = new ArrayList();
-
- coll1.add(123);
- coll1.add(456);
- coll1.add(new String("Tom"));
- coll1.add(false);
- coll1.add(new Person("Jerry", 20));
-
- boolean equals = coll.equals(coll1);
- System.out.println(equals);//true
- }
9、转成对象数组
Object[] toArray()
10、获取集合对象的哈希值
hashCode()
11、遍历
iterator():返回迭代器对象,用于集合遍历
- /*
- Collection接口中声明的方法测试
- */
- @SuppressWarnings("rawtypes")
- @Test
- public void test05() {
- Collection coll = new ArrayList();
-
- coll.add(123);
- coll.add(456);
- coll.add(new String("Tom"));
- coll.add(false);
- coll.add(new Person("Jerry", 20));
-
- //hashCode() : 返回当前对象的哈希值
- System.out.println(coll.hashCode());//-2061953047
-
- //toArray() : 集合 --> 数组
- Object[] arr = coll.toArray();//返回的是Object类型的数组
- for (int i = 0; i < arr.length; i++) {
- System.out.println(arr[i]);
- }
-
- //拓展 : 数组 --> 集合 Arrays.asList(T...t) 可变形参不推荐new,直接写数据即可
- List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
- System.out.println(list);//[AA, BB, CC]
-
- List<int[]> list1 = Arrays.asList(new int[]{123, 456});//认为是一个参数(一维数组)
- System.out.println(list1);//[[I@300ffa5d]
- System.out.println(list1.size());//1 --> 一个元素
-
- List<Integer> list2 = Arrays.asList(new Integer[]{123, 456});
- System.out.println(list2);//[123, 456]
- System.out.println(list2.size());//2 --> 两个元素
-
- //iterator() : 返回Iterator接口的实例,用于遍历集合元素 放在IteratorTest.java中测试
- }
Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合(不用于Map集合)中的元素。
GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。类似于“公交车上的售票员”、“火车上的乘务员”、“空姐”。
Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。
Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建Iterator 对象,则必须有一个被迭代的集合。
集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。
- @SuppressWarnings("rawtypes")
- @Test
- public void test(){
- Collection coll = new ArrayList();
-
- coll.add(123);
- coll.add(456);
- coll.add(new String("Tom"));
- coll.add(false);
- coll.add(new Person("Jerry", 20));
-
- Iterator iterator = coll.iterator();
-
- //next() : 游标下移 返回游标对应的元素
- //遍历方式一 :
- System.out.println(iterator.next());//123
- System.out.println(iterator.next());//456
- System.out.println(iterator.next());//Tom
- System.out.println(iterator.next());//false
- System.out.println(iterator.next());//Person{name='Jerry', age=20}
- //报异常 : NoSuchElementException
- // System.out.println(iterator.next());
-
- //遍历方式二 :
- //注意游标不会重置 这里重新获取一个迭代器对象
- Iterator iterator1 = coll.iterator();
- for (int i = 0; i < coll.size(); i++) {
- System.out.println(iterator1.next());
- }
-
- //hasNext() : 判断是否还有下一个元素
- //遍历方式三 : 推荐
- Iterator iterator2 = coll.iterator();
- while (iterator2.hasNext()){// 判断是否还有下一个元素
- //① 指针下移 ② 将下移以后集合位置上的元素返回
- System.out.println(iterator2.next());
- }
-
- }
测试Iterator中的remove() :
- @SuppressWarnings("rawtypes")
- @Test
- public void test2(){
- Collection coll = new ArrayList();
-
- coll.add(123);
- coll.add(456);
- coll.add(new String("Tom"));
- coll.add(false);
- coll.add(new Person("Jerry", 20));
-
- Iterator iterator = coll.iterator();
- //remove() : 可以在遍历的时候 删除集合中的元素 (删除游标对应的元素)
- while (iterator.hasNext()){
- Object obj = iterator.next();
- if ("Tom".equals(obj)){
- iterator.remove();
-
- //不能连续调用两次 会报异常 IllegalStateException
- // iterator.remove();
-
- }
- }
-
- Iterator iterator1 = coll.iterator();
- while (iterator1.hasNext()){
- System.out.println(iterator1.next());
- }
- }
注意 :
Iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的remove方法,不是集合对象的remove方法。
如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。
Java 5.0 提供了 foreach 循环 (增强for循环) 迭代访问 Collection和数组。
遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。
遍历集合的底层调用Iterator完成操作。
foreach还可以用来遍历数组。
- for (集合元素的类型 局部变量 : 要遍历的集合对象){
- //内部仍然调用了迭代器实现
- }
-
- 局部变量 : 盛装对应的元素 不是索引
遍历集合 :
- public class ForTest {
- public static void main(String[] args) {
- Collection coll = new ArrayList();
-
- coll.add(123);
- coll.add(456);
- coll.add(new String("Tom"));
- coll.add(false);
- coll.add(new Person("Jerry", 20));
-
- //for(集合元素的类型 局部变量 : 集合对象)
- /*
- 过程 : 内部用迭代器实现
- 先取coll中第一个元素赋值给obj
- 然后取第二个元素赋值给obj
- */
- for(Object obj : coll){
- System.out.println(obj);
- }
- }
- }
遍历数组 :
- @Test
- public void test(){
- int[] arr = new int[]{1, 2, 3, 4, 5, 6};
- //for(数组元素的类型 局部变量 : 数组对象)
- for (int i : arr){
- System.out.println(i);//这里的 i 就是对应的元素 不是索引
- }
- }
练习题 :
- @Test
- public void test01() {
- String[] arr = new String[]{"MM", "MM", "MM"};
-
- //方式一 : 普通for赋值
- // for (int i = 0; i < arr.length; i++) {
- // arr[i] = "GG";
- // }
-
- //方式二 : 增强for赋值
- for (String str : arr){
- str = "GG";//此时的赋值不是修改的数组本身 而是修改的局部变量
- }
-
- for (int i = 0; i < arr.length; i++) {
- System.out.println(arr[i]);
- }
- }
鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组,称作动态数组。
List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。
List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector。
- |----Collection接口 : 单列集合,用来存储一个一个的对象
- |----List接口 : 存储有序、可重复的数据 --> "动态"数组,替换原有的数组
- |----ArrayList
- |----LinkedList
- |----Vector
比较ArrayList、LinkedList、Vector三者的异同 ?
相同点 : 三个类都是实现了List接口,存储数据的特点相同 : 存储有序的、可重复的数据
不同点 :
ArrayList : 作为List接口的主要实现类,线程不安全的,效率高,底层使用Object[] elementData存储
Linkedlist : 对于频繁的增加和删除操作,使用此类效率比ArrayList高,底层使用双向链表存储
Vector : 作为List接口的古老实现类,线程安全的,效率低,底层使用Object[] elementData存储
ArrayList list = new ArrayList();
底层创建了长度是10的 Object[] 数组 elementData
- list.add(123);//elementData[0] = new Integer(123);
- ...
- list.add(456);//如果此次的添加导致底层elementData数组容量不够,则需要扩容
默认情况下,扩容为原来容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中 ( elementData = Arrays.copyOf(elementData, newCapacity) )。
结论 : 建议开发中使用带参的构造器 :
ArrayList list = new ArrayList(int capacity);
ArrayList list = new ArrayList();
底层Object[] elementData初始化为 {},并没有创建长度为10的数组
list.add(123);
当第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]
后续的添加与扩容操作与JDK7相同。
JDK 7中的ArrayList的对象的创建类似于单例模式的饿汉式
JDK 8中的ArrayList的对象的创建类似于单例模式的懒汉式,延迟了数组的创建,节省内存。
LinkedList list = new LinkedList();
内部声明了Node类型的first和last属性,默认值为null
list.add(123);
将123封装到Node中,创建了Node对象,其中Node的定义为如下,体现了LinkedList双向链表的实现原理。
- 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;
- }
- }
由于Vector容器是在JDK 1.0时就存在,而Collection接口框架在JDK 1.2时才有,是后才将Vector并入接口框架中的,所以对Vector的使用不多,Vector的特点是线程安全的。其底层实现与ArrayList的原理一样(Object[] 数组),在创建对象的时候默认也是创建一个容量为10的Object数组,但在扩容的时候与ArrayList不同的是默认扩容2倍而非1.5倍。
List除了从Collection集合继承的方法外,List集合里添加了一些根据索引来操作集合元素的方法 :
void add(int index, Object ele) : 在index位置插入ele元素
boolean addAll(int index, Collection eles) : 从index位置开始将eles中的所有元素添加进来
- @SuppressWarnings("rawtypes")
- @Test
- public void test01(){
- List list = new ArrayList<Object>();
-
- list.add(123);
- list.add(456);
- list.add(new String("Tom"));
- list.add(false);
- list.add(new Person("Jerry", 20));
-
- //void add(int index, Object ele) : 在index位置插入ele元素
- list.add(1, new String("insert"));
-
- System.out.println(list);//[123, insert, 456, Tom, false, Person{name='Jerry', age=20}]
-
- //boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
- List list1 = Arrays.asList(1, 2, 3);
- list.addAll(list1);
- System.out.println(list.size());
- }
Object get(int index) : 获取指定index位置的元素
int indexOf(Object obj) : 返回obj在集合中首次出现的位置
int lastIndexOf(Object obj) : 返回obj在当前集合中末次出现的位置
- @SuppressWarnings("rawtypes")
- @Test
- public void test02(){
- List list = new ArrayList<Object>();
-
- list.add(123);
- list.add(456);
- list.add(new String("Tom"));
- list.add(false);
- list.add(456);
- list.add(new Person("Jerry", 20));
-
- //Object get(int index):获取指定index位置的元素
- Object obj = list.get(1);
- System.out.println(obj);//456
-
- //int indexOf(Object obj):返回obj在集合中首次出现的位置 如果不存在返回-1
- int index = list.indexOf(456);
- System.out.println(index);//1
-
- //int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置 如果不存在返回-1
- int lastIndex = list.lastIndexOf(456);
- System.out.println(lastIndex);//4
- }
Object remove(int index) : 移除指定index位置的元素,并返回此元素
Object set(int index, Object ele):设置指定index位置的元素为ele,并返回被修改的元素
- @SuppressWarnings("rawtypes")
- @Test
- public void test03(){
- List list = new ArrayList<Object>();
-
- list.add(123);
- list.add(456);
- list.add(new String("Tom"));
- list.add(false);
- list.add(456);
- list.add(new Person("Jerry", 20));
-
- //Object remove(int index):移除指定index位置的元素,并返回此元素
- Object removeObj = list.remove(4);
- System.out.println(removeObj);
-
- System.out.println(list);//[123, 456, Tom, false, Person{name='Jerry', age=20}]
-
- //Object set(int index, Object ele):设置指定index位置的元素为ele 并返回被修改的元素
- Object obj = list.set(0, 789);
- System.out.println(obj);//123
- System.out.println(list);//[789, 456, Tom, false, Person{name='Jerry', age=20}]
- }
List subList(int fromIndex, int toIndex) : 返回从fromIndex到toIndex位置的子集合
- @SuppressWarnings("rawtypes")
- @Test
- public void test04(){
- List list = new ArrayList<Object>();
-
- list.add(123);
- list.add(456);
- list.add(new String("Tom"));
- list.add(false);
- list.add(456);
- list.add(new Person("Jerry", 20));
- System.out.println(list);//[123, 456, Tom, false, 456, Person{name='Jerry', age=20}]
-
- //List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
- List<Object> subList = list.subList(2, 4);
- System.out.println(subList);//[Tom, false]
- System.out.println(list);//[123, 456, Tom, false, 456, Person{name='Jerry', age=20}]
- }
遍历 : ①迭代器 ②增强for循环 ③普通for循环
- @SuppressWarnings("rawtypes")
- @Test
- public void test05(){
- List list = new ArrayList<Object>();
-
- list.add(123);
- list.add(456);
- list.add(new String("Tom"));
- list.add(false);
- list.add(456);
- list.add(new Person("Jerry", 20));
-
- //方式一 : 迭代器
- Iterator iterator = list.iterator();
- while (iterator.hasNext()){
- System.out.print(iterator.next() + " ");
- }
- System.out.println();
-
- //方式二 : 增强for循环
- for (Object obj : list){
- System.out.print(obj + " ");
- }
- System.out.println();
-
- //方式三 : 普通for循环
- for (int i = 0; i < list.size(); i++) {
- System.out.print(list.get(i) + " ");
- }
- System.out.println();
- }
总结,常用方法 :
- 增 : add(Object obj)
- 删 : remove(int index) / remove(Object obj)
- 改 : set(int index, Object ele)
- 查 : get(int index)
- 插 : add(int index, Object obj)
- 长度 : size()
- 遍历 : ①迭代器 ②增强for循环 ③普通for循环
区分remove中remove(int index) ,remove(Object obj)方法,例子 :
- public class ListExer {
- public static void main(String[] args) {
- List list = new ArrayList();
- list.add(1);
- list.add(2);
- list.add(3);
-
- updateList(list);
-
- System.out.println(list);//[1, 2]
- }
-
- public static void updateList(List list){
- list.remove(2);
-
- //list.remove(new Integer(2))
- }
- }
remove方法有 : remove(int index) ,remove(Object obj)两个,在方法外调用remove方法时首先默认调用的是不需要自动装箱的方法,如果想要调用remove(Object obj),则需要手动装箱。
Set接口是Collection的子接口,set接口没有提供额外的方法
Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set 集合中,则添加操作失败。
Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法
- |----Collection接口 : 单列集合,用来存储一个一个的对象
- |----Set接口 : 存储无序的、不可重复的数据 --> 高中讲的 "集合"
- |----HashSet
- |----LinkedHashSet
- |----TreeSet
比较HashSet、LinkedHashSet、TreeSet三者的异同 ?
相同点 : 三个类都是实现了Set接口,存储数据的特点相同 : 存储无序的、不可重复的数据
不同点 :
HashSet : 作为Set接口的主要实现类,线程不安全的,可以存储null值
LinkedHashSet : 作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历
TreeSet : 可以按照添加对象的指定属性,进行排序
以HashSet为例说明 :
(1) 无序性 : 不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。无序只是指在存储的时候不按索引顺序添加。
(2) 不可重复性 : 保证添加的元素按照equals()判断时,不能返回true,即相同的元素只能添加一个
- @SuppressWarnings("rawtypes")
- @Test
- public void test01(){
- Set set = new HashSet();
- set.add(456);
- set.add(123);
- set.add(123);
- set.add("AA");
- set.add("CC");
- set.add(new User("Tom", 12));
- set.add(new User("Tom", 12));
- set.add(129);
-
- System.out.println(set);
- //如果未重写equals方法 判断的地址值 所以不同
- //[AA, CC, User{name='Tom', age=12}, 129, User{name='Tom', age=12}, 456, 123]
- System.out.println(set);//重写equals方法之后 还是有两个
- //[AA, CC, User{name='Tom', age=12}, 129, User{name='Tom', age=12}, 456, 123]
- System.out.println(set);//重写了equals方法和hashCode方法之后 只有一个了
- //[AA, CC, 129, 456, 123, User{name='Tom', age=12}]
-
- }
以HashSet为例 :
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法 (& (size - 1) 等操作) 计算出在HashSet底层数组中的存放位置 (即 : 索引位置) ,判断数组此位置上是否已经有元素 :
如果此位置上没有其他元素,则元素a添加成功 (情况1)。如果此位置上有其他元素b (或以链表形式存在的多个元素),则比较元素a与元素b的hash值 :
如果hash值不相同,则元素a添加成功 (情况2)。如果hash值相同,则进而需要调用元素a所在类的equals()方法 :
如果equals()返回false,则元素a添加成功 (情况3)。如果equals()返回true,则元素a添加失败。
对于添加成功的情况2和情况3而言 : 元素a与已经存在指定索引位置上数据以链表的方式存储 :
jdk 7 : 元素a放到数组中,指向原来的元素。
jdk 8 : 原来的元素在数组中,指向元素a。
HashSet底层 : 数组 + 链表的结构(前提JDK 7)
实际上HashSet的实现原理就是HashMap,HashSet的数据实际上就是HashMap中的key,而value存放的是一个静态Object类对象,没有意义。
当向Set集合中添加数据时,在重写equals()方法时,建议同时重写hashCode(),并且保证两个方法的一致性 (相同的属性参与运算) ,这是因为在当对象放到HashSet集合或HashMap集合时,保证去重后的正确性。
了解hashCode方法和equals方法的两个重要的规范 :
规范一 : 两个对象相等,其hashCode一定相等
规范二 : 两个对象不等(equals方法返回false),并不要求这两个对象的hashCode得到不同的数
则得到如下推论 :
推论一 : hashCode相同的两个对象,不一定相等
推论二 : hashCode不同的两个对象,一定不相等
hashCode方法在Object类中定义,Object类中定义的hashCode方法调用的底层的C语言来计算出来一个随机数,这个随机数基本保证了不同的对象有不同的hashCode。如果我们不重写hashCode的话,在往HashSet集合中存放对象时,如果存放内容相等的两个对象,其hashCode不同,那两个对象都会存放成功,达不到去重效果。
重写hashCode()方法的基本原则 :
在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等。
对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
- @Override
- public int hashCode() {
- int result = name != null ? name.hashCode() : 0;
- result = 31 * result + age;
- return result;
- }
为什么用Eclipse/IDEA复写hashCode方法,有31这个数字?
选择系数的时候要选择尽量大的系数。因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。(减少冲突)
并且31只占用5bits,相乘造成数据溢出的概率较小。
31可以 由i * 31 == ( i << 5 ) - 1来表示,现在很多虚拟机里面都有做相关优化。(提高算法效率)
31是一个素数,素数作用就是如果我用一个数字来乘以这个素数,那么最终出来的结果只能被素数本身和被乘数还有1来整除!(减少冲突)
关于HashSet的使用,有关remove等方法的说明 : 先调用hashCode再用equals
- public class hashSetTest {
- public static void main(String[] args) {
- HashSet set = new HashSet();
-
- Person p1 = new Person(1001, "AA");
- Person p2 = new Person(1002, "BB");
-
- set.add(p1);
- set.add(p2);
- System.out.println(set);
- //[Person{id=1002, name='BB'}, Person{id=1001, name='AA'}]
- p1.name = "CC";
- set.remove(p1);
- System.out.println(set);
- //[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
- set.add(new Person(1001, "CC"));
- System.out.println(set);
- //[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
- set.add(new Person(1001, "AA"));
- System.out.println(set);
- //[Person{id=1002, name='BB'}, Person{id=1001, name='CC'},
- // Person{id=1001, name='CC'}, Person{id=1001, name='AA'}]
-
- }
- }
以上测试体现了HashSet的特点,当我们向HashSet中添加数据时,按照hashCode计算索引位置的规则进行添加的。当我们将添加以后的数据内容更改以后,其计算的hashCode将会改变。当我们remove改数据时,通过计算hashCode来寻找改元素计算得到的索引位置大概率不会是原来的位置,所以remove操作将会失败。当我们再向HashSet中添加一个更新后的数据时,由于原来更改后的数据没有占着索引位置,所以将会添加成功。当我们向HashSet中添加一个更新前的数据时,这时由于原来的数据占着其索引位置,那么就会比较hashCode,两个数据内容不同,所以hashCode不同,则添加成功。
LinkedHashSet是HashSet的子类,特点是在遍历的时候,可以按照添加的顺序遍历出来。原理是,在添加元素的同时还记录下了上一个添加的元素的地址,上一个添加的元素同时也记录了本次添加元素的地址。相当于形成了一个双向链表。
优点 : 对于频繁的遍历操作,LinkedHashSet效率高于HashSet。
- @SuppressWarnings("rawtypes")
- @Test
- public void test02(){
- Set set = new LinkedHashSet();
- set.add(456);
- set.add(123);
- set.add(123);
- set.add("AA");
- set.add("CC");
- set.add(new User("Tom", 12));
- set.add(new User("Tom", 12));
- set.add(129);
-
- System.out.println(set);
- //[456, 123, AA, CC, User{name='Tom', age=12}, 129]
- }
TreeSet : 可以按照添加对象的指定属性,进行排序
TreeSet底层使用的红黑树实现的,利用二叉树存储数据。调用compareTo()或compare()来判断元素是否相同。
在后续的调用contains方法、remove方法等,都是调用的compareTo或者compare方法来判断的。
向TreeSet中添加的数据,要求是同一个类的对象,不能添加不同类的对象,否则报ClassCastException异常。
向TreeSet中添加Integer对象 :
- @SuppressWarnings("rawtypes")
- @Test
- public void test01(){
- TreeSet set = new TreeSet();
-
- set.add(34);
- set.add(-34);
- set.add(43);
- set.add(11);
- set.add(8);
-
- System.out.println(set);//[-34, 8, 11, 34, 43]
-
- Iterator it = set.iterator();
- while(it.hasNext()){
- Object obj = it.next();
- System.out.print(obj + " ");
- }//-34 8 11 34 43 从小到大的顺序
- }
在创建TreeSet对象时,调用空参构造器,那么就会按照对象的自然排序方式进行操作。
向空参构造的TreeSet中添加自定义类User对象,其自定义的对象必须实现Comparable接口,否则报异常
ClassCastException: com.atguigu.study.User cannot be cast to java.lang.Comparable
自然排序中,比较两个对象是否相同的标准为 : compareTo()返回 0,不再是equals方法。两个对象是否相同的标准,是一个属性相同就认为两个对象相同,还是所有属性相同才认为两个对象相同,就要看自己在compareTo()方法中实现的逻辑了。
- @SuppressWarnings("rawtypes")
- @Test
- public void test02(){
- TreeSet set = new TreeSet();
-
- set.add(new User("Tom", 12));
- set.add(new User("Jerry", 32));
- set.add(new User("Jim", 2));
- set.add(new User("Mike", 65));
- set.add(new User("Jack", 33));
-
- Iterator it = set.iterator();
- while(it.hasNext()){
- Object obj = it.next();
- System.out.print(obj + " ");
- }
- //User{name='Jack', age=33} User{name='Jerry', age=32} User{name='Jim', age=2}
- // User{name='Mike', age=65} User{name='Tom', age=12}
- }
-
-
- public class User implements Comparable{
- private String name;
- private int age;
-
- public User(){}
- public User(String name, int age){
- this.age = age;
- this.name = name;
- }
-
- //按照名字从小到大排
- @Override
- public int compareTo(Object o) {
-
- if (o instanceof User) {
- User user = (User) o;
-
- return this.name.compareTo(user.name);
-
- } else throw new RuntimeException("传入数据类型不一致");
-
- }
- }
在创建TreeSet对象时,调用传入比较器的构造器,那么就会按照对象的定制排序方式进行操作。
定制排序中,比较两个对象是否相同的标准为 : compare()返回 0,不再是equals方法。
- @SuppressWarnings("rawtypes")
- @Test
- public void test03(){
- //按照年龄从小到大排 生成一个比较器
- Comparator com = new Comparator() {
- @Override
- public int compare(Object o1, Object o2) {
- if (o1 instanceof User && o2 instanceof User) {
- User user1 = (User) o1;
- User user2 = (User) o2;
-
- return Integer.compare(user1.getAge(), user2.getAge());
-
- } else throw new RuntimeException("传入数据类型不一致");
- }
- };
- //构造器中传入一个比较器 按照定制排序方式操作
- TreeSet set = new TreeSet(com);
-
- set.add(new User("Tom", 12));
- set.add(new User("Jerry", 32));
- set.add(new User("Jim", 2));
- set.add(new User("Mike", 65));
- set.add(new User("Jack", 33));
-
- Iterator it = set.iterator();
- while(it.hasNext()){
- Object obj = it.next();
- System.out.print(obj + " ");
- }
- //User{name='Jim', age=2} User{name='Tom', age=12} User{name='Jerry', age=32}
- // User{name='Jack', age=33} User{name='Mike', age=65}
- }
- |----Map : 双列数据,存储key-value对的数据 ---->类似于高中的函数 : y = f(x)
- |----HashMap : 作为Map的主要实现类,线程不安全,效率高,可以存储null的key和value
- |----LinkedHashMap : 保证在遍历map元素时,可以按照添加的顺序实现遍历
- 原理 : 在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素
- 对于频繁的遍历操作,此类执行效率高于HashMap。
- |----Hashtable : 作为古老的实现类,线程安全,效率低,不可以存储null的key和value
- |----Properties : 常用来处理配置文件。key-value都是String类型
- |----TreeMap : 保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
- 底层使用红黑树实现
-
- HashMap的底层 : 数组 + 链表 (JDK 7及之前)
- 数组 + 链表 + 红黑树(JDK 8)
key-value拆分理解 :
Map中的key : 无序的、不可重复的,使用Set存储所有的key (以HashMap为例,key所在的类要重写equals()和hashCode())。
Map中的value : 无序的、可重复的,使用Collection存储所有的value(value所在的类要重写equals()方法)。
一个键值对 : key-value构成了一个Entry对象。
Map中的Entry : 无序的、不可重复的,使用Set存储所有的Entry。
HashMap map = new HashMap();
在实例化之后,底层创建了长度是16的一维数组 Entry[] table。
- map.put()...
- map.put(key1, value1);
已经执行多次put以后的put(一个普通的put) 的执行过程 :
首先,调用key1所在类的hashCode()方法计算key1的哈希值,此哈希值经过某种算法(&)计算以后,得到在Entry数组中的存放位置。
如果此位置上的数据为空,此时的key1-value1(Entry)添加成功 (情况1) 。如果此位置上的数据不为空(意味着此位置上存在一个或多个数据以链表形式存在),比较当前key1和已经存在的一个或多个数据的哈希值。
如果key1的哈希值与已经存在的数据都不相同,此时key1-value1添加成功 (情况2) 。如果key1的哈希值与已经存在的某个数据 (key2-value2) 的哈希值相同,继续比较,调用key1所在类的equals()方法比较。
如果equals()返回false,此时key1-value1添加成功 (情况3) 。如果equals()返回true,则将使用value1替换value2。
- @SuppressWarnings("rawtypes")
- @Test
- public void test02(){
- HashMap map = new HashMap();
-
- //put具有修改功能
- map.put("Tom", 12);
- System.out.println(map);//{Tom=12}
- map.put("Tom", 45);
- System.out.println(map);//{Tom=45}
-
- }
补充 : 关于情况2和情况3,此时key1-value1和原来的数据以链表的方式 (JDK 7新元素在上) 存储。
在添加过程中,涉及到扩容问题,当超出临界值时 (且要存放的位置非空) 默认的扩容方式 : 扩容为原来数组的二倍,并将原有的数据复制过来。在复制的时候会重新计算各个元素的位置,重新存放。
JDK 8相较于JDK 7在底层实现方面的不同 :
new HashMap() 时底层没有创建一个长度为16的数组
JDK 8底层的数组是Node[],而非Entry[]
首次调用put()方法时,底层创建一个长度为16的数组
JDK 7底层结构只有 : 数组 + 链表,JDK 8底层结构 : 数组 + 链表 + 红黑树。当数组的某一个索引位置上的以链表形式存在的数据个数大于8,且当前数组的长度大于64时,此时,此索引位置上的所有数据改为使用红黑树存储。
HashMap源码中的重要常量 :
DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
DEFAULT_LOAD_FACTOR:HashMap的默认加载因子 (可以控制链表的多少),0.75 (比较稳定的数值)
threshold:扩容的临界值,容量 x 填充因子 = 0.75 x 16 = 12
TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树 ,8
MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量,64
负载因子值的大小对HashMap有什么影响 ?
负载因子的大小决定了HashMap的数据密度。
负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长,造成查询或插入时的比较次数增多,性能会下降。
负载因子越小,就越容易触发扩容,数据密度也越小,意味着发生碰撞的几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性能会更高。但是会浪费一定的内容空间。而且经常扩容也会影响性能,建议初始化预设大一点的空间。
按照其他语言的参考及研究经验,会考虑将负载因子设置为0.7~0.75,此时平均检索长度接近于常数。
能够按照添加的顺序进行遍历 :
- @SuppressWarnings("rawtypes")
- @Test
- public void test01(){
- Map map1 = new HashMap();
-
- map1.put(123, "AA");
- map1.put(456, "BB");
- map1.put(789, "CC");
-
- System.out.println(map1);//{789=CC, 456=BB, 123=AA}
-
- Map map2 = new LinkedHashMap();
-
- map2.put(123, "AA");
- map2.put(456, "BB");
- map2.put(789, "CC");
-
- System.out.println(map2);//{123=AA, 456=BB, 789=CC}
- }
在源码中,内部类Entry :
- static class Entry<K,V> extends HashMap.Node<K,V> {
- Entry<K,V> before, after;
- Entry(int hash, K key, V value, Node<K,V> next) {
- super(hash, key, value, next);
- }
- }
增加了两个指针来记录上一个添加的元素和下一个添加的元素地址,遍历的时候通过该指针实现按照添加顺序遍历。
添加、删除、修改操作:
Object put(Object key,Object value) : 将指定key-value添加到(或修改)当前map对象中
void putAll(Map m) : 将m中的所有key-value对存放到当前map中
Object remove(Object key) : 移除指定key的key-value对,并返回value
void clear() : 清空当前map中的所有数据
- /*
- Map接口中的常用方法测试
- */
- @SuppressWarnings("rawtypes")
- @Test
- public void test03(){
- Map map = new HashMap();
-
- //1. Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
- //添加
- map.put("AA", 123);
- map.put(45, 123);
- map.put("BB", 56);
- //修改
- map.put("AA", 87);
-
- System.out.println(map);//{AA=87, BB=56, 45=123}
-
- Map map1 = new HashMap();
- map1.put("CC", 123);
- map1.put("DD", 123);
-
- //2. void putAll(Map m):将m中的所有key-value对存放到当前map中
- map.putAll(map1);
- System.out.println(map);
- //{AA=87, BB=56, CC=123, DD=123, 45=123}
-
- //3. Object remove(Object key):移除指定key的key-value对,并返回value
- Object value = map.remove("CC");
- Object ee = map.remove("EE");
- System.out.println(value);//123
- System.out.println(ee);//null remove的key不存在将返回null
- System.out.println(map);//{AA=87, BB=56, DD=123, 45=123}
-
- //4. void clear():清空当前map中的所有数据
- map.clear();//与map = null操作不同
- System.out.println(map.size());//0
- System.out.println(map);//{}
- }
元素查询的操作:
Object get(Object key) : 获取指定key对应的value
boolean containsKey(Object key) : 是否包含指定的key
boolean containsValue(Object value) : 是否包含指定的value
int size() : 返回map中key-value对的个数
boolean isEmpty() : 判断当前map是否为空
boolean equals(Object obj) : 判断当前map和参数对象obj是否相等
- /*
- Map接口的常用方法测试
- */
- @SuppressWarnings("rawtypes")
- @Test
- public void test04(){
- Map map = new HashMap();
-
- map.put("AA", 123);
- map.put(45, 123);
- map.put("BB", 56);
-
- //1. Object get(Object key):获取指定key对应的value
- System.out.println(map.get("AA"));//123
- System.out.println(map.get("CC"));//null
-
- //2. boolean containsKey(Object key):是否包含指定的key
- boolean isExistKey = map.containsKey("AA");
- System.out.println(isExistKey);//true
-
- //3. boolean containsValue(Object value):是否包含指定的value
- boolean isExistVal = map.containsValue(123);
- System.out.println(isExistVal);//true
-
- //4. int size():返回map中key-value对的个数
- System.out.println(map.size());//3
-
- //5. boolean isEmpty():判断当前map是否为空
- boolean isEmpty = map.isEmpty();
- System.out.println(isEmpty);//false
-
- //6. boolean equals(Object obj):判断当前map和参数对象obj是否相等
- //要想为true则必须传入一个map且数据相同
- }
元视图操作的方法:
Set keySet() : 返回所有key构成的Set集合
Collection values() : 返回所有value构成的Collection集合
Set entrySet() : 返回所有key-value对构成的Set集合
- /*
- Map接口中的常用方法测试
- */
- @SuppressWarnings("rawtypes")
- @Test
- public void test05(){
- Map map = new HashMap();
-
- map.put("AA", 123);
- map.put(45, 123);
- map.put("BB", 56);
-
- //1. 遍历所有的Key集合 :
- Set keySet = map.keySet();
- for (Object o : keySet) {
- System.out.print(o + " ");
- }//AA BB 45
-
- System.out.println();
-
- //2. 遍历所有的values集 :
- Collection values = map.values();
- Iterator it = values.iterator();
- while(it.hasNext()){
- Object obj = it.next();
- System.out.print(obj + " ");
- }//123 56 123
-
- System.out.println();
-
- //3. 遍历所有的Entry集 :
- //方式一 :
- Set entrySet = map.entrySet();
- Iterator iterator = entrySet.iterator();
- while(iterator.hasNext()){
- Object obj = iterator.next();
- //entrySet集合中的元素都是entry
- Map.Entry entry = (Map.Entry) obj;
- System.out.println(entry.getKey() + " ---> " + entry.getValue());
- }
- /*
- AA ---> 123
- BB ---> 56
- 45 ---> 123
- */
-
- System.out.println();
-
- //方式二 :
- Iterator iterator1 = keySet.iterator();
- while(iterator1.hasNext()){
- Object key = iterator1.next();
- Object value = map.get(key);
-
- System.out.println(key + " ---> " + value);
- }
- /*
- AA ---> 123
- BB ---> 56
- 45 ---> 123
- */
- }
总结 : 常用方法
- 增 : put(Object key, Object value)
- 删 : remove(Object key)
- 改 : put(Object key, Object value)
- 查 : get(Object key)
- 长度 : size()
- 遍历 : keySet()/values()/entrySet()
向TreeMap中添加key-value,要求key必须是由同一个类创建的对象。
只能按照key来排,不能按照value排序。
- @SuppressWarnings("rawtypes")
- @Test
- public void test01(){
- TreeMap map = new TreeMap();
-
- User u1 = new User("Tom", 23);
- User u2 = new User("Jerry", 32);
- User u3 = new User("Jack", 20);
- User u4 = new User("Rose", 18);
-
- map.put(u1, 98);
- map.put(u2, 89);
- map.put(u3, 76);
- map.put(u4, 100);
-
- Set entrySet = map.entrySet();
- Iterator iterator = entrySet.iterator();
- while(iterator.hasNext()){
- Object obj = iterator.next();
- //entrySet集合中的元素都是entry
- Map.Entry entry = (Map.Entry) obj;
- System.out.println(entry.getKey() + " ---> " + entry.getValue());
- }
- /*
- User{name='Jack', age=20} ---> 76
- User{name='Jerry', age=32} ---> 89
- User{name='Rose', age=18} ---> 100
- User{name='Tom', age=23} ---> 98
- */
- }
- @SuppressWarnings("rawtypes")
- @Test
- public void test02(){
- TreeMap map = new TreeMap(new Comparator() {
- @Override
- public int compare(Object o1, Object o2) {
- if (o1 instanceof User && o2 instanceof User) {
- User u1 = (User) o1;
- User u2 = (User) o2;
- //按照年龄排
- return Integer.compare(u1.getAge(), u2.getAge());
-
- } else throw new RuntimeException("传入的类型不一致");
- }
- });
-
- User u1 = new User("Tom", 23);
- User u2 = new User("Jerry", 32);
- User u3 = new User("Jack", 20);
- User u4 = new User("Rose", 18);
-
- map.put(u1, 98);
- map.put(u2, 89);
- map.put(u3, 76);
- map.put(u4, 100);
-
- Set entrySet = map.entrySet();
- Iterator iterator = entrySet.iterator();
- while(iterator.hasNext()){
- Object obj = iterator.next();
- //entrySet集合中的元素都是entry
- Map.Entry entry = (Map.Entry) obj;
- System.out.println(entry.getKey() + " ---> " + entry.getValue());
- }
- /*
- User{name='Rose', age=18} ---> 100
- User{name='Jack', age=20} ---> 76
- User{name='Tom', age=23} ---> 98
- User{name='Jerry', age=32} ---> 89
- */
- }
Properties 类是 Hashtable 的子类,该对象用于处理属性文件
由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key 和 value 都是字符串类型
存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法
- public class PropertiesTest {
- public static void main(String[] args) {
- FileInputStream fis = null;
-
- try {
- Properties pros = new Properties();
- fis = new FileInputStream("jdbc.properties");
-
- pros.load(fis);//加载流对应的文件
-
- String name = pros.getProperty("name");//获取value
- String password = pros.getProperty("password");
-
- System.out.println("name = " + name + " password = " + password);
-
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- if (fis != null)
- try {
- fis.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
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 处元素进行交换
- @SuppressWarnings("rawtypes")
- @Test
- public void test01(){
- List list = new ArrayList();
-
- list.add(123);
- list.add(43);
- list.add(765);
- list.add(-97);
- list.add(0);
-
- System.out.println(list);//[123, 43, 765, -97, 0]
- //1. reverse(List) : 反转List中元素的顺序
- Collections.reverse(list);
- System.out.println(list);//[0, -97, 765, 43, 123]
-
- //2. shuffle(List) : 对List集合元素进行随机排序 (随机化)
- Collections.shuffle(list);
- System.out.println(list);//[-97, 0, 43, 123, 765]
-
- //3. sort(List) : 根据元素的自然顺序对指定List集合元素按升序排序
- Collections.sort(list);
- System.out.println(list);//[-97, 0, 43, 123, 765]
-
- //4. swap(List,int,int) : 将指定list集合中的i处元素和j处元素进行交换
- Collections.swap(list, 0, 1);
- System.out.println(list);//[0, -97, 43, 123, 765]
- }
Object max(Collection) : 根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator) : 根据Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object) : 返回指定集合中指定元素的出现次数
- @SuppressWarnings("rawtypes")
- @Test
- public void test02(){
- List list = new ArrayList();
-
- list.add(123);
- list.add(43);
- list.add(765);
- list.add(765);
- list.add(765);
- list.add(-97);
- list.add(0);
- System.out.println(list);
- //1. Object max(Collection) : 根据元素的自然顺序,返回给定集合中的最大元素
- Comparable max = Collections.max(list);
- System.out.println(max);//765
-
- //2. Object min(Collection)
- Comparable min = Collections.min(list);
- System.out.println(min);//-97
-
- //3. int frequency(Collection,Object) : 返回指定集合中指定元素的出现次数
- int frequency = Collections.frequency(list, 765);
- System.out.println(frequency);//3
-
- }
void copy(List dest,List src) : 将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal) : 使用新值替换List对象的所有旧值
- @SuppressWarnings("rawtypes")
- @Test
- public void test03(){
- List list = new ArrayList();
-
- list.add(123);
- list.add(43);
- list.add(765);
- list.add(-97);
- list.add(0);
-
- System.out.println(list);
-
- //1. void copy(List dest,List src) : 将src中的内容复制到dest中
- //错误 报异常 底层要判断 src.size() > dest.size()
- //java.lang.IndexOutOfBoundsException: Source does not fit in dest
- // List dest = new ArrayList();
- // Collections.copy(dest, list);
- //利用Object数组把dest的size撑开
- List dest = Arrays.asList(new Object[list.size()]);
- Collections.copy(dest, list);
- System.out.println(dest);//[123, 43, 765, -97, 0]
-
- //2. boolean replaceAll(List list,Object oldVal,Object newVal) : 使用新值替换List对象的所有旧值
- Collections.replaceAll(list, 123, 0);
- System.out.println(list);//[0, 43, 765, -97, 0]
- }
Collections 类中提供了多个synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。
- @SuppressWarnings("rawtypes")
- @Test
- public void test04(){
- List list = new ArrayList();
-
- list.add(123);
- list.add(43);
- list.add(765);
- list.add(-97);
- list.add(0);
-
- //synchronizedXxx() : 该方法可使将指定集合包装成线程同步的集合
- //返回的synchronizedList即为线程安全的List
- List synchronizedList = Collections.synchronizedList(list);
- System.out.println(synchronizedList);//[123, 43, 765, -97, 0]
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。