上海嘉定网站建设公司,有没有知道网址的,怎样先做网站后买域名,湘潭网站建设 尖端磐石网络提示#xff1a;文章写完后#xff0c;目录可以自动生成#xff0c;如何生成可参考右边的帮助文档 文章目录 01、Java 集合框架概述1.1、集合框架与数组的对比及概述1.2、集合框架涉及到的API 02、Collection接口方法2.1、Collection接口中的常用方法12.2、Collection接口中… 提示文章写完后目录可以自动生成如何生成可参考右边的帮助文档 文章目录 01、Java 集合框架概述1.1、集合框架与数组的对比及概述1.2、集合框架涉及到的API 02、Collection接口方法2.1、Collection接口中的常用方法12.2、Collection接口中的常用方法22.3、Collection接口中的常用方法32.4、Collection接口中的常用方法4 03、Iterator迭代器接口3.1、使用Iterator遍历Collection3.2、迭代器Iterator的执行原理3.3、Iterator遍历集合的两种错误写法3.4、Iterator迭代器remove()的使用3.5、新特性foreach循环遍历集合或数组 04、Collection子接口之一List接口4.1、List接口常用实现类的对比4.2、ArrayList的源码分析4.3、LinkedList的源码分析4.4、Vector的源码分析4.5、List接口中的常用方法测试4.6、List的一个面试小题 05、Collection子接口之二Set接口5.1、Set接口实现类的对比5.2、Set的无序性与不可重复性的理解5.3、HashSet中元素的添加过程5.4、关于hashCode()和equals()的重写5.4.1、重写hashCode() 方法的基本原则5.4.2、重写equals() 方法的基本原则5.4.3、Eclipse/IDEA工具里hashCode()的重写 5.5、LinkedHashSet的使用5.6、TreeSet的自然排序5.7、TreeSet的定制排序5.8、TreeSet的课后练习5.9、Set课后两道面试题 06、Map接口6.1、Map接口及其多个实现类的对比6.2、Map中存储的key-value的特点6.3、Map实现类之一HashMap6.4、HashMap的底层实现原理6.4.1、HashMap在JDK7中的底层实现原理6.4.2、HashMap在JDK8中的底层实现原理 6.7、LinkedHashMap的底层实现原理了解6.8、Map中的常用方法16.9、Map中的常用方法26.10、TreeMap两种添加方式的使用6.12、Hashtable6.13、Properties处理属性文件 07、Collections工具类7.1、Collections工具类常用方法的测试 01、Java 集合框架概述
1.1、集合框架与数组的对比及概述
/*** 一、集合的框架** 1.集合、数组都是对多个数据进行存储操作的结构简称Java容器。* 说明此时的存储主要是指能存层面的存储不涉及到持久化的存储.txt,.jpg,.avi,数据库中** 2.1数组在存储多个数据封面的特点* 》一旦初始化以后它的长度就确定了。* 》数组一旦定义好它的数据类型也就确定了。我们就只能操作指定类型的数据了。* 比如String[] arr;int[] str;* 2.2数组在存储多个数据方面的特点* 》一旦初始化以后其长度就不可修改。* 》数组中提供的方法非常有限对于添加、删除、插入数据等操作非常不便同时效率不高。* 》获取数组中实际元素的个数的需求数组没有现成的属性或方法可用* 》数组存储数据的特点有序、可重复。对于无序、不可重复的需求不能满足。**/
1.2、集合框架涉及到的API
Java 集合可分为Collection 和Map 两种体系 Collection接口单列数据定义了存取一组对象的方法的集合 List元素有序、可重复的集合Set元素无序、不可重复的集合 Map接口双列数据保存具有映射关系“key-value对”的集合 1、Collection接口继承树 2、Map接口继承树 /**** 二、集合框架* ---Collection接口单列集合用来存储一个一个的对象* ---List接口存储有序的、可重复的数据。 --“动态”数组* ---ArrayList、LinkedList、Vector** ---Set接口存储无序的、不可重复的数据 --高中讲的“集合”* ---HashSet、LinkedHashSet、TreeSet** ---Map接口双列集合用来存储一对(key - value)一对的数据 --高中函数y f(x)* ---HashMap、LinkedHashMap、TreeMap、Hashtable、Properties**/
02、Collection接口方法
Collection 接口是List、Set 和Queue 接口的父接口该接口里定义的方法既可用于操作Set 集合也可用于操作List 和Queue 集合。JDK不提供此接口的任何直接实现而是提供更具体的子接口(如Set和List)实现。在Java5 之前Java 集合会丢失容器中所有对象的数据类型把所有对象都当成Object 类型处理从JDK 5.0 增加了泛型以后Java 集合可以记住容器中对象的数据类型。
2.1、Collection接口中的常用方法1
添加 add(Objec tobj)addAll(Collection coll) 获取有效元素的个数 int size() 清空集合 void clear() 是否是空集合 boolean isEmpty() 是否包含某个元素 boolean contains(Object obj)是通过元素的equals方法来判断是否是同一个对象boolean containsAll(Collection c)也是调用元素的equals方法来比较的。拿两个集合的元素挨个比较。 删除 boolean remove(Object obj) 通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素boolean removeAll(Collection coll)取当前集合的差集 取两个集合的交集 boolean retainAll(Collection c)把交集的结果存在当前集合中不影响c 集合是否相等 boolean equals(Object obj) 转成对象数组 Object[] toArray() 获取集合对象的哈希值 hashCode() 遍历 iterator()返回迭代器对象用于集合遍历
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;/**** 三、Collection接口中的方法的使用**/
public class CollectionTest {Testpublic void test1(){Collection coll new ArrayList();//add(Object e):将元素e添加到集合coll中coll.add(AA);coll.add(BB);coll.add(123); //自动装箱coll.add(new Date());//size():获取添加的元素的个数System.out.println(coll.size()); //4//addAll(Collection coll1):将coll1集合中的元素添加到当前的集合中Collection coll1 new ArrayList();coll1.add(456);coll1.add(CC);coll.addAll(coll1);System.out.println(coll.size()); //6System.out.println(coll);//clear():清空集合元素coll.clear();//isEmpty():判断当前集合是否为空System.out.println(coll.isEmpty());}
}
2.2、Collection接口中的常用方法2 1、Person类 import java.util.Objects;public class Person {private String name;private int age;public Person() {super();}public Person(String name, int age) {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;}Overridepublic String toString() {return Person{ name name \ , age age };}Overridepublic boolean equals(Object o) {System.out.println(Person equals()....);if (this o) return true;if (o null || getClass() ! o.getClass()) return false;Person person (Person) o;return age person.age Objects.equals(name, person.name);}Overridepublic int hashCode() {return Objects.hash(name, age);}
} 2、测试类 import org.junit.Test;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;/*** Collection接口中声明的方法的测试** 结论* 向Collection接口的实现类的对象中添加数据obj时要求obj所在类要重写equals().*/
public class CollectinoTest {Testpublic void test(){Collection coll new ArrayList();coll.add(123);coll.add(456);// Person p new Person(Jerry,20);
// coll.add(p);coll.add(new Person(Jerry,20));coll.add(new String(Tom));coll.add(false);//1.contains(Object obj):判断当前集合中是否包含obj//我们在判断时会调用obj对象所在类的equals()。boolean contains coll.contains(123);System.out.println(contains);System.out.println(coll.contains(new String(Tam)));
// System.out.println(coll.contains(p));//trueSystem.out.println(coll.contains(new Person(Jerry,20)));//false --true//2.containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中。Collection coll1 Arrays.asList(123,4567);System.out.println(coll.containsAll(coll1));}}
2.3、Collection接口中的常用方法3 1、Person类 import java.util.Objects;public class Person {private String name;private int age;public Person() {super();}public Person(String name, int age) {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;}Overridepublic String toString() {return Person{ name name \ , age age };}Overridepublic boolean equals(Object o) {System.out.println(Person equals()....);if (this o) return true;if (o null || getClass() ! o.getClass()) return false;Person person (Person) o;return age person.age Objects.equals(name, person.name);}Overridepublic int hashCode() {return Objects.hash(name, age);}
} 2、测试类 import org.junit.Test;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;/*** Collection接口中声明的方法的测试** 结论* 向Collection接口的实现类的对象中添加数据obj时要求obj所在类要重写equals().**/
public class CollectinoTest {Testpublic void test2(){//3.remove(Object obj):从当前集合中移除obj元素。Collection coll new ArrayList();coll.add(123);coll.add(456);coll.add(new Person(Jerry,20));coll.add(new String(Tom));coll.add(false);coll.remove(1234);System.out.println(coll);coll.remove(new Person(Jerry,20));System.out.println(coll);//4. removeAll(Collection coll1):差集从当前集合中移除coll1中所有的元素。Collection coll1 Arrays.asList(123,456);coll.removeAll(coll1);System.out.println(coll);}Testpublic void test3(){Collection coll new ArrayList();coll.add(123);coll.add(456);coll.add(new Person(Jerry,20));coll.add(new String(Tom));coll.add(false);//5.retainAll(Collection coll1):交集获取当前集合和coll1集合的交集并返回给当前集合
// Collection coll1 Arrays.asList(123,456,789);
// coll.retainAll(coll1);
// System.out.println(coll);//6.equals(Object obj):要想返回true需要当前集合和形参集合的元素都相同。Collection coll1 new ArrayList();coll1.add(456);coll1.add(123);coll1.add(new Person(Jerry,20));coll1.add(new String(Tom));coll1.add(false);System.out.println(coll.equals(coll1));}}
2.4、Collection接口中的常用方法4 1、Person类 import java.util.Objects;public class Person {private String name;private int age;public Person() {super();}public Person(String name, int age) {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;}Overridepublic String toString() {return Person{ name name \ , age age };}Overridepublic boolean equals(Object o) {System.out.println(Person equals()....);if (this o) return true;if (o null || getClass() ! o.getClass()) return false;Person person (Person) o;return age person.age Objects.equals(name, person.name);}Overridepublic int hashCode() {return Objects.hash(name, age);}
}
import org.junit.Test;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;/*** Collection接口中声明的方法的测试** 结论* 向Collection接口的实现类的对象中添加数据obj时要求obj所在类要重写equals().**/
public class CollectinoTest {Testpublic void test4(){Collection coll new ArrayList();coll.add(123);coll.add(456);coll.add(new Person(Jerry,20));coll.add(new String(Tom));coll.add(false);//7.hashCode():返回当前对象的哈希值System.out.println(coll.hashCode());//8.集合 ---数组toArray()Object[] arr coll.toArray();for(int i 0;i arr.length;i){System.out.println(arr[i]);}//拓展数组 ---集合:调用Arrays类的静态方法asList()ListString list Arrays.asList(new String[]{AA, BB, CC});System.out.println(list);List arr1 Arrays.asList(123, 456);System.out.println(arr1);//[123, 456]List arr2 Arrays.asList(new int[]{123, 456});System.out.println(arr2.size());//1List arr3 Arrays.asList(new Integer[]{123, 456});System.out.println(arr3.size());//2//9.iterator():返回Iterator接口的实例用于遍历集合元素。放在IteratorTest.java中测试}
}
03、Iterator迭代器接口
Iterator对象称为迭代器(设计模式的一种)主要用于遍历Collection 集合中的元素。GOF给迭代器模式的定义为提供一种方法访问一个容器(container)对象中各个元素而又不需暴露该对象的内部细节。迭代器模式就是为容器而生。类似于“公交车上的售票员”、“火车上的乘务员”、“空姐”。Collection接口继承了java.lang.Iterable接口该接口有一个iterator()方法那么所有实现了Collection接口的集合类都有一个iterator()方法用以返回一个实现了Iterator接口的对象。Iterator 仅用于遍历集合Iterator本身并不提供承装对象的能力。如果需要创建Iterator 对象则必须有一个被迭代的集合。集合对象每次调用iterator()方法都得到一个全新的迭代器对象默认游标都在集合的第一个元素之前。
3.1、使用Iterator遍历Collection
import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/*** 集合元素的遍历操作使用迭代器Iterator接口* 内部的方法hasNext()和 next()**/
public class IteratorTest {Testpublic void test(){Collection coll new ArrayList();coll.add(123);coll.add(456);coll.add(new Person(Jerry,20));coll.add(new String(Tom));coll.add(false);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
// //因为在调用it.next()方法之前必须要调用it.hasNext()进行检测。若不调用且下一条记录无效直接调用it.next()会抛出NoSuchElementException异常。
// System.out.println(iterator.next());//方式二不推荐
// for(int i 0;i coll.size();i){
// System.out.println(iterator.next());
// }//方式三推荐while(iterator.hasNext()){System.out.println(iterator.next());}}
}
3.2、迭代器Iterator的执行原理 3.3、Iterator遍历集合的两种错误写法
import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/*** 集合元素的遍历操作使用迭代器Iterator接口* 1.内部的方法hasNext()和 next()* 2.集合对象每次调用iterator()方法都得到一个全新的迭代器对象默认游标都在集合的第一个元素之前。*/
public class IteratorTest {Testpublic void test2(){Collection coll new ArrayList();coll.add(123);coll.add(456);coll.add(new Person(Jerry,20));coll.add(new String(Tom));coll.add(false);//错误方式一
// Iterator iterator coll.iterator();
// while(iterator.next() ! null){
// System.out.println(iterator.next());
// }//错误方式二//集合对象每次调用iterator()方法都得到一个全新的迭代器对象默认游标都在集合的第一个元素之前。while(coll.iterator().hasNext()){System.out.println(coll.iterator().next());}}
}
3.4、Iterator迭代器remove()的使用
import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/*** 集合元素的遍历操作使用迭代器Iterator接口* 1.内部的方法hasNext()和 next()* 2.集合对象每次调用iterator()方法都得到一个全新的迭代器对象默认游标都在集合的第一个元素之前。* 3.内部定义了remove(),可以在遍历的时候删除集合中的元素。此方法不同于集合直接调用remove()*/
public class IteratorTest {//测试Iterator中的remove()方法Testpublic void test3(){Collection coll new ArrayList();coll.add(123);coll.add(456);coll.add(new Person(Jerry,20));coll.add(new String(Tom));coll.add(false);//删除集合中”Tom”//如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法// 再调用remove都会报IllegalStateException。Iterator iterator coll.iterator();while(iterator.hasNext()){
// iterator.remove();Object obj iterator.next();if(Tom.equals(obj)){iterator.remove();
// iterator.remove(); }}//遍历集合iterator coll.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
} 注意 Iterator可以删除集合的元素但是是遍历过程中通过迭代器对象的remove方法不是集合对象的remove方法。如果还未调用next()或在上一次调用next方法之后已经调用了remove方法再调用remove都会报IllegalStateException。
3.5、新特性foreach循环遍历集合或数组
Java 5.0 提供了foreach循环迭代访问Collection和数组。遍历操作不需获取Collection或数组的长度无需使用索引访问元素。遍历集合的底层调用Iterator完成操作。foreach还可以用来遍历数组。 import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;/*** jdk 5.0 新增了foreach循环用于遍历集合、数组**/
public class ForTest {Testpublic void test(){Collection coll new ArrayList();coll.add(123);coll.add(456);coll.add(new Person(Jerry,20));coll.add(new String(Tom));coll.add(false);//for(集合元素的类型 局部变量 : 集合对象),内部仍然调用了迭代器。for(Object obj : coll){System.out.println(obj);}}Testpublic void test2(){int[] arr new int[]{1,2,3,4,5,6};//for(数组元素的类型 局部变量 : 数组对象)for(int i : arr){System.out.println(i);}}//练习题Testpublic void test3(){String[] arr new String[]{SS,KK,RR};// //方式一普通for赋值
// for(int i 0;i arr.length;i){
// arr[i] HH;
// }//方式二增强for循环for(String s : arr){s HH;}for(int i 0;i arr.length;i){System.out.println(arr[i]);}}
}
04、Collection子接口之一List接口
鉴于Java中数组用来存储数据的局限性我们通常使用List替代数组List集合类中元素有序、且可重复集合中的每个元素都有其对应的顺序索引。List容器中的元素都对应一个整数型的序号记载其在容器中的位置可以根据序号存取容器中的元素。JDK API中List接口的实现类常用的有ArrayList、LinkedList和Vector。
4.1、List接口常用实现类的对比
/*** 1. List接口框架** |----Collection接口单列集合用来存储一个一个的对象* |----List接口存储有序的、可重复的数据。 --“动态”数组,替换原有的数组* |----ArrayList作为List接口的主要实现类线程不安全的效率高底层使用Object[] elementData存储* |----LinkedList对于频繁的插入、删除操作使用此类效率比ArrayList高底层使用双向链表存储* |----Vector作为List接口的古老实现类线程安全的效率低底层使用Object[] elementData存储*** 面试题比较ArrayList、LinkedList、Vector三者的异同* 同三个类都是实现了List接口存储数据的特点相同存储有序的、可重复的数据* 不同见上**/
4.2、ArrayList的源码分析
ArrayList是List 接口的典型实现类、主要实现类本质上ArrayList是对象引用的一个”变长”数组
/** * 2.ArrayList的源码分析* 2.1 jdk 7情况下* 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)** 2.2 jdk 8中ArrayList的变化* ArrayList list new ArrayList();//底层Object[] elementData初始化为{}.并没有创建长度为10的数组** list.add(123);//第一次调用add()时底层才创建了长度10的数组并将数据123添加到elementData[0]* ...* 后续的添加和扩容操作与jdk 7 无异。* 2.3 小结jdk7中的ArrayList的对象的创建类似于单例的饿汉式而jdk8中的ArrayList的对象* 的创建类似于单例的懒汉式延迟了数组的创建节省内存。* */
4.3、LinkedList的源码分析
对于频繁的插入或删除元素的操作建议使用LinkedList类效率较高LinkedList双向链表内部没有声明数组而是定义了Node类型的first和last用于记录首末元素。同时定义内部类Node作为LinkedList中保存数据的基本结构。 /*** 3.LinkedList的源码分析* LinkedList list new LinkedList(); 内部声明了Node类型的first和last属性默认值为null* list.add(123);//将123封装到Node中创建了Node对象。** 其中Node定义为体现了LinkedList的双向链表的说法* private static class NodeE {* E item;* NodeE next;* NodeE prev;** Node(NodeE prev, E element, NodeE next) {* this.item element;* this.next next; //next变量记录下一个元素的位置* this.prev prev; //prev变量记录前一个元素的位置* }* }*/
4.4、Vector的源码分析
Vector 是一个古老的集合JDK1.0就有了。大多数操作与ArrayList相同区别之处在于Vector是线程安全的。在各种list中最好把ArrayList作为缺省选择。当插入、删除频繁时使用LinkedListVector总是比ArrayList慢所以尽量避免使用。
/** * 4.Vector的源码分析jdk7和jdk8中通过Vector()构造器创建对象时底层都创建了长度为10的数组。* 在扩容方面默认扩容为原来的数组长度的2倍。*/
4.5、List接口中的常用方法测试 List除了从Collection集合继承的方法外List 集合里添加了一些根据索引来操作集合元素的方法。 void add(intindex, Object ele):在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位置的元素为eleList subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;/**** 5.List接口的常用方法*/
public class ListTest {/**** void add(int index, Object ele):在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位置的子集合** 总结常用方法* 增add(Object obj)* 删remove(int index) / remove(Object obj)* 改set(int index, Object ele)* 查get(int index)* 插add(int index, Object ele)* 长度size()* 遍历① Iterator迭代器方式* ② 增强for循环* ③ 普通的循环**/Testpublic void test3(){ArrayList list new ArrayList();list.add(123);list.add(456);list.add(AA);//方式一Iterator迭代器方式Iterator iterator list.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}System.out.println(***************);//方式二增强for循环for(Object obj : list){System.out.println(obj);}System.out.println(***************);//方式三普通for循环for(int i 0;i list.size();i){System.out.println(list.get(i));}}Testpublic void tets2(){ArrayList list new ArrayList();list.add(123);list.add(456);list.add(AA);list.add(new Person(Tom,12));list.add(456);//int indexOf(Object obj):返回obj在集合中首次出现的位置。如果不存在返回-1.int index list.indexOf(4567);System.out.println(index);//int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置。如果不存在返回-1.System.out.println(list.lastIndexOf(456));//Object remove(int index):移除指定index位置的元素并返回此元素Object obj list.remove(0);System.out.println(obj);System.out.println(list);//Object set(int index, Object ele):设置指定index位置的元素为elelist.set(1,CC);System.out.println(list);//List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的左闭右开区间的子集合List subList list.subList(2, 4);System.out.println(subList);System.out.println(list);}Testpublic void test(){ArrayList list new ArrayList();list.add(123);list.add(456);list.add(AA);list.add(new Person(Tom,12));list.add(456);System.out.println(list);//void add(int index, Object ele):在index位置插入ele元素list.add(1,BB);System.out.println(list);//boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来List list1 Arrays.asList(1, 2, 3);list.addAll(list1);
// list.add(list1);System.out.println(list.size());//9//Object get(int index):获取指定index位置的元素System.out.println(list.get(2));}
}
4.6、List的一个面试小题 1、面试题1 请问ArrayList/LinkedList/Vector的异同谈谈你的理解ArrayList底层是什么扩容机制Vector和ArrayList的最大区别? /*** 请问ArrayList/LinkedList/Vector的异同谈谈你的理解* ArrayList底层是什么扩容机制Vector和ArrayList的最大区别?* * ArrayList和LinkedList的异同二者都线程不安全相对线程安全的Vector执行效率高。* 此外ArrayList是实现了基于动态数组的数据结构LinkedList基于链表的数据结构。* 对于随机访问get和setArrayList觉得优于LinkedList因为LinkedList要移动指针。* 对于新增和删除操作add(特指插入)和removeLinkedList比较占优势因为ArrayList要移动数据。* * ArrayList和Vector的区别Vector和ArrayList几乎是完全相同的,* 唯一的区别在于Vector是同步类(synchronized)属于强同步类。* 因此开销就比ArrayList要大访问要慢。正常情况下,* 大多数的Java程序员使用ArrayList而不是Vector,* 因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大小的2倍空间* 而ArrayList是1.5倍。Vector还有一个子类Stack。*/ 2、面试题2 import org.junit.Test;import java.util.ArrayList;
import java.util.List;public class ListEver {/*** 区分List中remove(int index)和remove(Object obj)*/Testpublic void testListRemove() {List list new ArrayList();list.add(1);list.add(2);list.add(3);updateList(list);System.out.println(list);//}private void updateList(List list) {
// list.remove(2);list.remove(new Integer(2));}
}
05、Collection子接口之二Set接口
Set接口是Collection的子接口set接口没有提供额外的方法Set 集合不允许包含相同的元素如果试把两个相同的元素加入同一个Set 集合中则添加操作失败。Set 判断两个对象是否相同不是使用 运算符而是根据equals() 方法
5.1、Set接口实现类的对比
/*** 1.Set接口的框架* |----Collection接口单列集合用来存储一个一个的对象* |----Set接口存储无序的、不可重复的数据 --高中讲的“集合”* |----HashSet作为Set接口的主要实现类线程不安全的可以存储null值* |----LinkedHashSet作为HashSet的子类遍历其内部数据时可以按照添加的顺序遍历* 对于频繁的遍历操作LinkedHashSet效率高于HashSet.* |----TreeSet可以按照添加对象的指定属性进行排序。*/
5.2、Set的无序性与不可重复性的理解 1、测试类 import org.junit.Test;import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;/**** 1.Set接口中没有定义额外的方法使用的都是Collection中声明过的方法。**/
public class SetTest {/*** 一、Set:存储无序的、不可重复的数据* 1.无序性不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加而是根据数据的哈希值决定的。** 2.不可重复性保证添加的元素按照equals()判断时不能返回true.即相同的元素只能添加一个。** 二、添加元素的过程以HashSet为例***/Testpublic void test(){Set set new HashSet();set.add(123);set.add(456);set.add(fgd);set.add(book);set.add(new User(Tom,12));set.add(new User(Tom,12));set.add(129);Iterator iterator set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
} 2、User类 public class User{private String name;private int age;public User() {}public User(String name, int age) {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;}Overridepublic String toString() {return User{ name name \ , age age };}Overridepublic boolean equals(Object o) {System.out.println(User equals()....);if (this o) return true;if (o null || getClass() ! o.getClass()) return false;User user (User) o;if (age ! user.age) return false;return name ! null ? name.equals(user.name) : user.name null;}Overridepublic int hashCode() { int result name ! null ? name.hashCode() : 0;result 31 * result age;return result;}
}
5.3、HashSet中元素的添加过程
HashSet是Set 接口的典型实现大多数时候使用Set 集合时都使用这个实现类。HashSet按Hash 算法来存储集合中的元素因此具有很好的存取、查找、删除性能。HashSet具有以下特点不能保证元素的排列顺序 HashSet不是线程安全的集合元素可以是null 底层也是数组初始容量为16当如果使用率超过0.7516*0.7512就会扩大容量为原来的2倍。16扩容为32依次为64,128…等HashSet 集合判断两个元素相等的标准两个对象通过hashCode() 方法比较相等并且两个对象的equals() 方法返回值也相等。对于存放在Set容器中的对象对应的类一定要重写equals()和hashCode(Object obj)方法以实现对象相等规则。即“相等的对象必须具有相等的散列码”。
/*** 一、Set:存储无序的、不可重复的数据* 1.无序性不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加而是根据数据的哈希值决定的。** 2.不可重复性保证添加的元素按照equals()判断时不能返回true.即相同的元素只能添加一个。** 二、添加元素的过程以HashSet为例* 我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法计算元素a的哈希值* 此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置即为索引位置判断* 数组此位置上是否已经有元素* 如果此位置上没有其他元素则元素a添加成功。 ---情况1* 如果此位置上有其他元素b(或以链表形式存在的多个元素则比较元素a与元素b的hash值* 如果hash值不相同则元素a添加成功。---情况2* 如果hash值相同进而需要调用元素a所在类的equals()方法* equals()返回true,元素a添加失败* equals()返回false,则元素a添加成功。---情况2** 对于添加成功的情况2和情况3而言元素a 与已经存在指定索引位置上数据以链表的方式存储。* jdk 7 :元素a放到数组中指向原来的元素。* jdk 8 :原来的元素在数组中指向元素a* 总结七上八下** HashSet底层数组链表的结构。**/ 5.4、关于hashCode()和equals()的重写
5.4.1、重写hashCode() 方法的基本原则
在程序运行时同一个对象多次调用hashCode() 方法应该返回相同的值。当两个对象的equals() 方法比较返回true 时这两个对象的hashCode() 方法的返回值也应相等。对象中用作equals() 方法比较的Field都应该用来计算hashCode 值。
5.4.2、重写equals() 方法的基本原则 以自定义的Customer类为例何时需要重写equals() 当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候总是要改写hashCode()根据一个类的equals方法改写后两个截然不同的实例有可能在逻辑上是相等的但是根据Object.hashCode()方法它们仅仅是两个对象。因此违反了“相等的对象必须具有相等的散列码”。结论复写equals方法的时候一般都需要同时复写hashCode方法。通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。
5.4.3、Eclipse/IDEA工具里hashCode()的重写 以Eclipse/IDEA为例在自定义类中可以调用工具自动重写equals和hashCode。问题为什么用Eclipse/IDEA复写hashCode方法有31这个数字 选择系数的时候要选择尽量大的系数。因为如果计算出来的hash地址越大所谓的“冲突”就越少查找起来效率也会提高。减少冲突并且31只占用5bits,相乘造成数据溢出的概率较小。31可以由i*31 (i5)-1来表示,现在很多虚拟机里面都有做相关优化。提高算法效率31是一个素数素数作用就是如果我用一个数字来乘以这个素数那么最终出来的结果只能被素数本身和被乘数还有1来整除(减少冲突)
/*** 2.要求向Set(主要指HashSet、LinkedHashSet)中添加的数据其所在的类一定要重写hashCode()和equals()* 要求重写的hashCode()和equals()尽可能保持一致性相等的对象必须具有相等的散列码* 重写两个方法的小技巧对象中用作 equals() 方法比较的 Field都应该用来计算 hashCode 值。*/
5.5、LinkedHashSet的使用
LinkedHashSet是HashSet的子类LinkedHashSet根据元素的hashCode值来决定元素的存储位置但它同时使用双向链表维护元素的次序这使得元素看起来是以插入顺序保存的。LinkedHashSet插入性能略低于HashSet但在迭代访问Set 里的全部元素时有很好的性能。LinkedHashSet不允许集合元素重复。 1、测试类 import org.junit.Test;import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;public class SetTest {/*** LinkedHashSet的使用* LinkedHashSet作为HashSet的子类在添加数据的同时每个数据还维护了两个引用记录此数据前一个* 数据和后一个数据。* 优点对于频繁的遍历操作LinkedHashSet效率高于HashSet*/Testpublic void test2(){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);Iterator iterator set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
}
public class User{private String name;private int age;public User() {}public User(String name, int age) {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;}Overridepublic String toString() {return User{ name name \ , age age };}Overridepublic boolean equals(Object o) {System.out.println(User equals()....);if (this o) return true;if (o null || getClass() ! o.getClass()) return false;User user (User) o;if (age ! user.age) return false;return name ! null ? name.equals(user.name) : user.name null;}Overridepublic int hashCode() { //return name.hashCode() age;int result name ! null ? name.hashCode() : 0;result 31 * result age;return result;}
}
5.6、TreeSet的自然排序
TreeSet是SortedSet接口的实现类TreeSet可以确保集合元素处于排序状态。TreeSet底层使用红黑树结构存储数据新增的方法如下(了解) Comparator comparator()Object first()Object last()Object lower(Object e)Object higher(Object e)SortedSet subSet(fromElement, toElement)SortedSet headSet(toElement)SortedSet tailSet(fromElement) TreeSet两种排序方法自然排序和定制排序。默认情况下TreeSet采用自然排序。TreeSet和后面要讲的TreeMap采用红黑树的存储结构特点有序查询速度比List快自然排序TreeSet会调用集合元素的compareTo(Object obj) 方法来比较元素之间的大小关系然后将集合元素按升序(默认情况)排列。如果试图把一个对象添加到TreeSet时则该对象的类必须实现Comparable 接口。 实现Comparable 的类必须实现compareTo(Object obj) 方法两个对象即通过compareTo(Object obj) 方法的返回值来比较大小。 Comparable 的典型实现 BigDecimal、BigInteger 以及所有的数值型对应的包装类按它们对应的数值大小进行比较Character按字符的unicode值来进行比较Booleantrue 对应的包装类实例大于false 对应的包装类实例String按字符串中字符的unicode 值进行比较Date、Time后边的时间、日期比前面的时间、日期大 向TreeSet中添加元素时只有第一个元素无须比较compareTo()方法后面添加的所有元素都会调用compareTo()方法进行比较。因为只有相同类的两个实例才会比较大小所以向TreeSet中添加的应该是同一个类的对象。对于TreeSet集合而言它判断两个对象是否相等的唯一标准是两个对象通过compareTo(Object obj) 方法比较返回值。当需要把一个对象放入TreeSet中重写该对象对应的equals() 方法时应保证该方法与compareTo(Object obj) 方法有一致的结果如果两个对象通过equals() 方法比较返回true则通过compareTo(Object obj) 方法比较应返回0。否则让人难以理解。 红黑树 1、测试类 import org.junit.Test;import java.util.Iterator;
import java.util.TreeSet;/*** 1.向TreeSet中添加的数据要求是相同类的对象。* 2.两种排序方式自然排序实现Comparable接口 和 定制排序Comparator* 3.自然排序中比较两个对象是否相同的标准为compareTo()返回0.不再是equals().* 4.定制排序中比较两个对象是否相同的标准为compare()返回0.不再是equals().*/
public class TreeSetTest {Testpublic void test() {TreeSet set new TreeSet();//失败不能添加不同类的对象
// set.add(123);
// set.add(456);
// set.add(AA);
// set.add(new User(Tom,12));//举例一
// set.add(34);
// set.add(-34);
// set.add(43);
// set.add(11);
// set.add(8);//举例二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));set.add(new User(Jack,56));Iterator iterator set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
} 2、User类 public class User implements Comparable{private String name;private int age;public User() {}public User(String name, int age) {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;}Overridepublic String toString() {return User{ name name \ , age age };}Overridepublic boolean equals(Object o) {System.out.println(User equals()....);if (this o) return true;if (o null || getClass() ! o.getClass()) return false;User user (User) o;if (age ! user.age) return false;return name ! null ? name.equals(user.name) : user.name null;}Overridepublic int hashCode() { //return name.hashCode() age;int result name ! null ? name.hashCode() : 0;result 31 * result age;return result;}//按照姓名从大到小排列,年龄从小到大排列Overridepublic int compareTo(Object o) {if (o instanceof User) {User user (User) o;
// return this.name.compareTo(user.name); //按照姓名从小到大排列
// return -this.name.compareTo(user.name); //按照姓名从大到小排列int compare -this.name.compareTo(user.name); //按照姓名从大到小排列if(compare ! 0){ //年龄从小到大排列return compare;}else{return Integer.compare(this.age,user.age);}} else {throw new RuntimeException(输入的类型不匹配);}}
}
5.7、TreeSet的定制排序
TreeSet的自然排序要求元素所属的类实现Comparable接口如果元素所属的类没有实现Comparable接口或不希望按照升序(默认情况)的方式排列元素或希望按照其它属性大小进行排序则考虑使用定制排序。定制排序通过Comparator接口来实现。需要重写compare(T o1,T o2)方法。利用int compare(T o1,T o2)方法比较o1和o2的大小如果方法返回正整数则表示o1大于o2如果返回0表示相等返回负整数表示o1小于o2。要实现定制排序需要将实现Comparator接口的实例作为形参传递给TreeSet的构造器。此时仍然只能向TreeSet中添加类型相同的对象。否则发生ClassCastException异常。使用定制排序判断两个元素相等的标准是通过Comparator比较两个元素返回了0。 1、测试类 import org.junit.Test;import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;/*** 1.向TreeSet中添加的数据要求是相同类的对象。* 2.两种排序方式自然排序实现Comparable接口 和 定制排序Comparator* 3.自然排序中比较两个对象是否相同的标准为compareTo()返回0.不再是equals().* 4.定制排序中比较两个对象是否相同的标准为compare()返回0.不再是equals().*/
public class TreeSetTest {Testpublic void tets2(){Comparator com new Comparator() {//按照年龄从小到大排列Overridepublic 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(输入的数据类型不匹配);}}};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(Mary,33));set.add(new User(Jack,33));set.add(new User(Jack,56));Iterator iterator set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
} 2、User类 public class User implements Comparable{private String name;private int age;public User() {}public User(String name, int age) {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;}Overridepublic String toString() {return User{ name name \ , age age };}Overridepublic boolean equals(Object o) {System.out.println(User equals()....);if (this o) return true;if (o null || getClass() ! o.getClass()) return false;User user (User) o;if (age ! user.age) return false;return name ! null ? name.equals(user.name) : user.name null;}Overridepublic int hashCode() { //return name.hashCode() age;int result name ! null ? name.hashCode() : 0;result 31 * result age;return result;}//按照姓名从大到小排列,年龄从小到大排列Overridepublic int compareTo(Object o) {if (o instanceof User) {User user (User) o;
// return this.name.compareTo(user.name); //按照姓名从小到大排列
// return -this.name.compareTo(user.name); //按照姓名从大到小排列int compare -this.name.compareTo(user.name); //按照姓名从大到小排列if(compare ! 0){ //年龄从小到大排列return compare;}else{return Integer.compare(this.age,user.age);}} else {throw new RuntimeException(输入的类型不匹配);}}
} 2、User类 public class User implements Comparable{private String name;private int age;public User() {}public User(String name, int age) {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;}Overridepublic String toString() {return User{ name name \ , age age };}Overridepublic boolean equals(Object o) {System.out.println(User equals()....);if (this o) return true;if (o null || getClass() ! o.getClass()) return false;User user (User) o;if (age ! user.age) return false;return name ! null ? name.equals(user.name) : user.name null;}Overridepublic int hashCode() { //return name.hashCode() age;int result name ! null ? name.hashCode() : 0;result 31 * result age;return result;}//按照姓名从大到小排列,年龄从小到大排列Overridepublic int compareTo(Object o) {if (o instanceof User) {User user (User) o;
// return this.name.compareTo(user.name); //按照姓名从小到大排列
// return -this.name.compareTo(user.name); //按照姓名从大到小排列int compare -this.name.compareTo(user.name); //按照姓名从大到小排列if(compare ! 0){ //年龄从小到大排列return compare;}else{return Integer.compare(this.age,user.age);}} else {throw new RuntimeException(输入的类型不匹配);}}
}
5.8、TreeSet的课后练习 1、MyDate类 /*** MyDate类包含:* private成员变量year,month,day并为每一个属性定义getter, setter 方法*/
public class MyDate implements Comparable{private int year;private int month;private int day;public int getYear() {return year;}public void setYear(int year) {this.year year;}public int getMonth() {return month;}public void setMonth(int month) {this.month month;}public int getDay() {return day;}public void setDay(int day) {this.day day;}public MyDate() {}public MyDate(int year, int month, int day) {this.year year;this.month month;this.day day;}Overridepublic String toString() {return MyDate{ year year , month month , day day };}Overridepublic int compareTo(Object o) {if(o instanceof MyDate){MyDate m (MyDate)o;//比较年int minusYear this.getYear() - m.getYear();if(minusYear ! 0){return minusYear;}//比较月int minusMonth this.getMonth() - m.getMonth();if(minusMonth ! 0){return minusMonth;}//比较日return this.getDay() - m.getDay();}throw new RuntimeException(传入的数据类型不一致);}
} 2、Employee类 /*** 定义一个Employee类。* 该类包含private成员变量name,age,birthday* 其中birthday 为MyDate 类的对象* 并为每一个属性定义getter, setter 方法* 并重写toString 方法输出name, age, birthday*/
public class Employee implements Comparable{private String name;private int age;private MyDate birthday;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;}public MyDate getBirthday() {return birthday;}public void setBirthday(MyDate birthday) {this.birthday birthday;}public Employee() {}public Employee(String name, int age, MyDate birthday) {this.name name;this.age age;this.birthday birthday;}Overridepublic String toString() {return Employee{ name name \ , age age , birthday birthday };}//按name排序Overridepublic int compareTo(Object o){if(o instanceof Employee){Employee e (Employee)o;return this.name.compareTo(e.name);}
// return 0;throw new RuntimeException(传入的数据类型不一致);}
} 3、测试类 import org.junit.Test;import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;/*** 创建该类的5 个对象并把这些对象放入TreeSet 集合中* 下一章TreeSet 需使用泛型来定义分别按以下两种方式* 对集合中的元素进行排序并遍历输出** 1). 使Employee 实现Comparable 接口并按name 排序* 2). 创建TreeSet 时传入Comparator对象按生日日期的先后排序。*/
public class EmployeeTest {//问题二按生日日期的先后排序Testpublic void test2(){TreeSet set new TreeSet(new Comparator() {Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof Employee o2 instanceof Employee){Employee e1 (Employee)o1;Employee e2 (Employee)o2;MyDate b1 e1.getBirthday();MyDate b2 e2.getBirthday();//方式一
// //比较年
// int minusYear b1.getYear() - b2.getYear();
// if(minusYear ! 0){
// return minusYear;
// }
//
// //比较月
// int minusMonth b1.getMonth() - b2.getMonth();
// if(minusMonth ! 0){
// return minusMonth;
// }
//
// //比较日
// return b1.getDay() - b2.getDay();//方式二return b1.compareTo(b2);}
// return 0;throw new RuntimeException(传入的数据类型不一致);}});Employee e1 new Employee(wangxianzhi,41,new MyDate(334,5,4));Employee e2 new Employee(simaqian,43,new MyDate(-145,7,12));Employee e3 new Employee(yanzhenqin,44,new MyDate(709,5,9));Employee e4 new Employee(zhangqian,51,new MyDate(-179,8,12));Employee e5 new Employee(quyuan,21,new MyDate(-340,12,4));set.add(e1);set.add(e2);set.add(e3);set.add(e4);set.add(e5);Iterator iterator set.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}}//问题一使用自然排序Testpublic void test(){TreeSet set new TreeSet();Employee e1 new Employee(wangxianzhi,41,new MyDate(334,5,4));Employee e2 new Employee(simaqian,43,new MyDate(-145,7,12));Employee e3 new Employee(yanzhenqin,44,new MyDate(709,5,9));Employee e4 new Employee(zhangqian,51,new MyDate(-179,8,12));Employee e5 new Employee(quyuan,21,new MyDate(-340,12,4));set.add(e1);set.add(e2);set.add(e3);set.add(e4);set.add(e5);Iterator iterator set.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}}
}
5.9、Set课后两道面试题 练习在List内去除重复数字值要求尽量简单 import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;public class CollectionTest {//练习在List内去除重复数字值要求尽量简单public static List duplicateList(List list) {HashSet set new HashSet();set.addAll(list);return new ArrayList(set);}Testpublic void test2(){List list new ArrayList();list.add(new Integer(1));list.add(new Integer(2));list.add(new Integer(2));list.add(new Integer(4));list.add(new Integer(4));List list2 duplicateList(list);for (Object integer : list2) {System.out.println(integer);}}
} 2、面试题 import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;public class CollectionTest {Testpublic void test3(){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);p1.name CC;set.remove(p1);System.out.println(set);set.add(new Person(1001,CC));System.out.println(set);set.add(new Person(1001,AA));System.out.println(set);}
}
06、Map接口 6.1、Map接口及其多个实现类的对比
import org.junit.Test;import java.util.HashMap;
import java.util.Map;/*** 一、Map的实现类的结构* |----Map:双列数据存储key-value对的数据 ---类似于高中的函数y f(x)* |----HashMap:作为Map的主要实现类线程不安全的效率高存储null的key和value* |----LinkedHashMap:保证在遍历map元素时可以按照添加的顺序实现遍历。* 原因在原有的HashMap底层结构基础上添加了一对指针指向前一个和后一个元素。* 对于频繁的遍历操作此类执行效率高于HashMap。* |----TreeMap:保证按照添加的key-value对进行排序实现排序遍历。此时考虑key的自然排序或定制排序* 底层使用红黑树* |----Hashtable:作为古老的实现类线程安全的效率低不能存储null的key和value* |----Properties:常用来处理配置文件。key和value都是String类型*** HashMap的底层数组链表 jdk7及之前* 数组链表红黑树 jdk 8** 面试题* 1. HashMap的底层实现原理* 2. HashMap 和 Hashtable的异同* 3. CurrentHashMap 与 Hashtable的异同暂时不讲**/
public class MapTest {Testpublic void test(){Map map new HashMap();
// map new Hashtable();map.put(null,123);}
}
6.2、Map中存储的key-value的特点
Map与Collection并列存在。用于保存具有映射关系的数据:key-valueMap 中的key 和value 都可以是任何引用类型的数据Map 中的key 用Set来存放不允许重复即同一个Map 对象所对应的类须重写hashCode()和equals()方法常用String类作为Map的“键”key 和value 之间存在单向一对一关系即通过指定的key 总能找到唯一的、确定的valueMap接口的常用实现类HashMap、TreeMap、LinkedHashMap和Properties。其中HashMap是Map 接口使用频率最高的实现类 /*** 二、Map结构的理解* Map中的key:无序的、不可重复的使用Set存储所有的key --- key所在的类要重写equals()和hashCode() 以HashMap为例* Map中的value:无序的、可重复的使用Collection存储所有的value ---value所在的类要重写equals()* 一个键值对key-value构成了一个Entry对象。* Map中的entry:无序的、不可重复的使用Set存储所有的entry**/
6.3、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() 方法返回truehashCode值也相等。HashMap判断两个value相等的标准是两个value 通过equals() 方法返回true。
6.4、HashMap的底层实现原理 JDK 7及以前版本HashMap是数组链表结构(即为链地址法) JDK 8版本发布以后HashMap是数组链表红黑树实现。 HashMap源码中的重要常量 /** DEFAULT_INITIAL_CAPACITY : HashMap的默认容量16* DEFAULT_LOAD_FACTORHashMap的默认加载因子0.75* threshold扩容的临界值容量*填充因子16 * 0.75 12* TREEIFY_THRESHOLDBucket中链表长度大于该默认值转化为红黑树:8* MIN_TREEIFY_CAPACITY桶中的Node被树化时最小的hash表容量:64
*/
6.4.1、HashMap在JDK7中的底层实现原理
HashMap的内部存储结构其实是数组和链表的结合。当实例化一个HashMap时系统会创建一个长度为Capacity的Entry数组这个长度在哈希表中被称为容量(Capacity)在这个数组中可以存放元素的位置我们称之为“桶”(bucket)每个bucket都有自己的索引系统可以根据索引快速的查找bucket中的元素。每个bucket中存储一个元素即一个Entry对象但每一个Entry对象可以带一个引用变量用于指向下一个元素因此在一个桶中就有可能生成一个Entry链。而且新添加的元素作为链表的head。添加元素的过程 向HashMap中添加entry1(keyvalue)需要首先计算entry1中key的哈希值(根据key所在类的hashCode()计算得到)此哈希值经过处理以后得到在底层Entry[]数组中要存储的位置i。如果位置i上没有元素则entry1直接添加成功。如果位置i上已经存在entry2(或还有链表存在的entry3entry4)则需要通过循环的方法依次比较entry1中key的hash值和其他的entry的hash值。如果彼此hash值不同则直接添加成功。如果hash值相同继续比较二者是否equals。如果返回值为true则使用entry1的value去替换equals为true的entry的value。如果遍历一遍以后发现所有的equals返回都为false,则entry1仍可添加成功。entry1指向原有的entry元素。
/** 三、HashMap的底层实现原理以jdk7为例说明* HashMap map new HashMap():* 在实例化以后底层创建了长度是16的一维数组Entry[] table。* ...可能已经执行过多次put...* map.put(key1,value1):* 首先调用key1所在类的hashCode()计算key1哈希值此哈希值经过某种算法计算以后得到在Entry数组中的存放位置。* 如果此位置上的数据为空此时的key1-value1添加成功。 ----情况1* 如果此位置上的数据不为空(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据* 的哈希值* 如果key1的哈希值与已经存在的数据的哈希值都不相同此时key1-value1添加成功。----情况2* 如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同继续比较调用key1所在类的equals(key2)方法比较* 如果equals()返回false:此时key1-value1添加成功。----情况3* 如果equals()返回true:使用value1替换value2。** 补充关于情况2和情况3此时key1-value1和原来的数据以链表的方式存储。** 在不断的添加过程中会涉及到扩容问题当超出临界值(且要存放的位置非空)时扩容。默认的扩容方式扩容为原来容量的2倍并将原有的数据复制过来。**//*** HashMap的扩容* 当HashMap中的元素越来越多的时候hash冲突的几率也就越来越高* 因为数组的长度是固定的。所以为了提高查询的效率* 就要对HashMap的数组进行扩容而在HashMap数组扩容之后* 最消耗性能的点就出现了原数组中的数据必须重新计算其在新数组中的位置* 并放进去这就是resize。* * 那么HashMap什么时候进行扩容呢* 当HashMap中的元素个数超过数组大小(数组总大小length,* 不是数组中个数size)*loadFactor时就 会 进 行 数 组 扩 容* loadFactor的默认值(DEFAULT_LOAD_FACTOR)为0.75这是一个折中的取值。* 也就是说默认情况下数组大小(DEFAULT_INITIAL_CAPACITY)为16* 那么当HashMap中元素个数超过16*0.7512这个值就是代码中的threshold值* 也叫做临界值的时候就把数组的大小扩展为2*1632即扩大一倍* 然后重新计算每个元素在数组中的位置而这是一个非常消耗性能的操作* 所以如果我们已经预知HashMap中元素的个数* 那么预设元素的个数能够有效的提高HashMap的性能。*/
6.4.2、HashMap在JDK8中的底层实现原理
HashMap的内部存储结构其实是数组链表红黑树的结合。当实例化一个HashMap时会初始化initialCapacity和loadFactor在put第一对映射关系时系统会创建一个长度为initialCapacity的Node数组这个长度在哈希表中被称为容量(Capacity)在这个数组中可以存放元素的位置我们称之为“桶”(bucket)每个bucket都有自己的索引系统可以根据索引快速的查找bucket中的元素每个bucket中存储一个元素即一个Node对象但每一个Node对象可以带一个引用变量next用于指向下一个元素因此在一个桶中就有可能生成一个Node链。也可能是一个一个TreeNode对象每一个TreeNode对象可以有两个叶子结点left和right因此在一个桶中就有可能生成一个TreeNode树。而新添加的元素作为链表的last或树的叶子结点。那么HashMap什么时候进行扩容和树形化呢 当HashMap中的元素个数超过数组大小(数组总大小length,不是数组中个数size)loadFactor时就会进行数组扩容loadFactor的默认值(DEFAULT_LOAD_FACTOR)为0.75这是一个折中的取值。也就是说默认情况下数组大小(DEFAULT_INITIAL_CAPACITY)为16那么当HashMap中元素个数超过160.7512这个值就是代码中的threshold值也叫做临界值的时候就把数组的大小扩展为2*1632即扩大一倍然后重新计算每个元素在数组中的位置而这是一个非常消耗性能的操作所以如果我们已经预知HashMap中元素的个数那么预设元素的个数能够有效的提高HashMap的性能。当HashMap中的其中一个链的对象个数如果达到了8个此时如果capacity没有达到64那么HashMap会先扩容解决如果已经达到了64那么这个链会变成红黑树结点类型由Node变成TreeNode类型。当然如果当映射关系被移除后下次resize方法时判断树的结点个数低于6个也会把红黑树再转为链表。 关于映射关系的key是否可以修改answer不要修改 映射关系存储到HashMap中会存储key的hash值这样就不用在每次查找时重新计算每一个Entry或NodeTreeNode的hash值了因此如果已经put到Map中的映射关系再修改key的属性而这个属性又参与hashcode值的计算那么会导致匹配不上。
/* 总结* jdk8 相较于jdk7在底层实现方面的不同* 1.new HashMap():底层没有创建一个长度为16的数组* 2.jdk 8底层的数组是Node[],而非Entry[]* 3.首次调用put()方法时底层创建长度为16的数组* 4.jdk7底层结构只有数组链表。jdk8中底层结构数组链表红黑树。* 4.1形成链表时七上八下jdk7:新的元素指向旧的元素。jdk8旧的元素指向新的元素* 4.2当数组的某一个索引位置上的元素以链表形式存在的数据个数 8 且当前数组的长度 64时此时此索引位置上的所数据改为使用红黑树存储。*/
6.7、LinkedHashMap的底层实现原理了解
LinkedHashMap是HashMap的子类在HashMap存储结构的基础上使用了一对双向链表来记录添加元素的顺序与LinkedHashSet类似LinkedHashMap可以维护Map 的迭代顺序迭代顺序与Key-Value 对的插入顺序一致HashMap中的内部类Node
/** 四、LinkedHashMap的底层实现原理了解* 源码中* static class EntryK,V extends HashMap.NodeK,V {* EntryK,V before, after;//能够记录添加的元素的先后顺序* Entry(int hash, K key, V value, NodeK,V next) {* super(hash, key, value, next);* }* } */
import org.junit.Test;import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;public class MapTest {Testpublic void test2(){Map map new HashMap();map new LinkedHashMap();map.put(123,AA);map.put(345,BB);map.put(12,CC);System.out.println(map);}
}
6.8、Map中的常用方法1
import org.junit.Test;import java.util.*;
/*** 五、Map中定义的方法* 添加、删除、修改操作* 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中的所有数据* 元素查询的操作* 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是否相等* 元视图操作的方法* Set keySet()返回所有key构成的Set集合* Collection values()返回所有value构成的Collection集合* Set entrySet()返回所有key-value对构成的Set集合**/
public class MapTest {/*** 元素查询的操作* 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是否相等*/Testpublic void test4(){Map map new HashMap();map.put(AA,123);map.put(45,123);map.put(BB,56);// Object get(Object key)System.out.println(map.get(45));//containsKey(Object key)boolean isExist map.containsKey(BB);System.out.println(isExist);isExist map.containsValue(123);System.out.println(isExist);map.clear();System.out.println(map.isEmpty());}/*** 添加、删除、修改操作* 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中的所有数据*/Testpublic void test3(){Map map new HashMap();//添加map.put(AA,123);map.put(45,123);map.put(BB,56);//修改map.put(AA,87);System.out.println(map);Map map1 new HashMap();map1.put(CC,123);map1.put(DD,456);map.putAll(map1);System.out.println(map);//remove(Object key)Object value map.remove(CC);System.out.println(value);System.out.println(map);//clear()map.clear();//与map null操作不同System.out.println(map.size());System.out.println(map);}
}
6.9、Map中的常用方法2
import org.junit.Test;import java.util.*;
/*** 五、Map中定义的方法* 添加、删除、修改操作* 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中的所有数据* 元素查询的操作* 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是否相等* 元视图操作的方法* Set keySet()返回所有key构成的Set集合* Collection values()返回所有value构成的Collection集合* Set entrySet()返回所有key-value对构成的Set集合** 总结常用方法* 添加put(Object key,Object value)* 删除remove(Object key)* 修改put(Object key,Object value)* 查询get(Object key)* 长度size()* 遍历keySet() / values() / entrySet()** 面试题* 1. HashMap的底层实现原理* 2. HashMap 和 Hashtable的异同* 1.HashMap与Hashtable都实现了Map接口。由于HashMap的非线程安全性效率上可能高于Hashtable。Hashtable的方法是Synchronize的而HashMap不是在多个线程访问Hashtable时不需要自己为它的方法实现同步而HashMap 就必须为之提供外同步。* 2.HashMap允许将null作为一个entry的key或者value而Hashtable不允许。* 3.HashMap把Hashtable的contains方法去掉了改成containsvalue和containsKey。因为contains方法容易让人引起误解。* 4.Hashtable继承自Dictionary类而HashMap是Java1.2引进的Map interface的一个实现。* 5.Hashtable和HashMap采用的hash/rehash算法都大概一样所以性能不会有很大的差异。** 3. CurrentHashMap 与 Hashtable的异同暂时不讲**/
public class MapTest {/*** 元视图操作的方法* Set keySet()返回所有key构成的Set集合* Collection values()返回所有value构成的Collection集合* Set entrySet()返回所有key-value对构成的Set集合*/Testpublic void test5(){Map map new HashMap();map.put(AA,123);map.put(45,1234);map.put(BB,56);//遍历所有的key集keySet()Set set map.keySet();Iterator iterator set.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}System.out.println(*****************);//遍历所有的values集values()Collection values map.values();for(Object obj : values){System.out.println(obj);}System.out.println(***************);//遍历所有的key-values//方式一Set entrySet map.entrySet();Iterator iterator1 entrySet.iterator();while (iterator1.hasNext()){Object obj iterator1.next();//entrySet集合中的元素都是entryMap.Entry entry (Map.Entry) obj;System.out.println(entry.getKey() ---- entry.getValue());}System.out.println(/);//方式二Set keySet map.keySet();Iterator iterator2 keySet.iterator();while(iterator2.hasNext()){Object key iterator2.next();Object value map.get(key);System.out.println(key value);}}
}
6.10、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。 1、User类 public class User implements Comparable{private String name;private int age;public User() {}public User(String name, int age) {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;}Overridepublic String toString() {return User{ name name \ , age age };}Overridepublic boolean equals(Object o) {System.out.println(User equals()....);if (this o) return true;if (o null || getClass() ! o.getClass()) return false;User user (User) o;if (age ! user.age) return false;return name ! null ? name.equals(user.name) : user.name null;}Overridepublic int hashCode() { //return name.hashCode() age;int result name ! null ? name.hashCode() : 0;result 31 * result age;return result;}//按照姓名从大到小排列,年龄从小到大排列Overridepublic int compareTo(Object o) {if(o instanceof User){User user (User)o;
// return -this.name.compareTo(user.name);int compare -this.name.compareTo(user.name);if(compare ! 0){return compare;}else{return Integer.compare(this.age,user.age);}}else{throw new RuntimeException(输入的类型不匹配);}}
} 2、测试类 import org.junit.Test;import java.util.*;public class TreeMapTest {/*** 向TreeMap中添加key-value要求key必须是由同一个类创建的对象* 因为要按照key进行排序自然排序 、定制排序*///自然排序Testpublic void test(){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 iterator1 entrySet.iterator();while (iterator1.hasNext()){Object obj iterator1.next();Map.Entry entry (Map.Entry) obj;System.out.println(entry.getKey() ---- entry.getValue());}}//定制排序Testpublic void test2(){TreeMap map new TreeMap(new Comparator() {Overridepublic 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());}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 iterator1 entrySet.iterator();while (iterator1.hasNext()){Object obj iterator1.next();Map.Entry entry (Map.Entry) obj;System.out.println(entry.getKey() ---- entry.getValue());}}
}
6.12、Hashtable
Hashtable是个古老的Map 实现类JDK1.0就提供了。不同于HashMapHashtable是线程安全的。Hashtable实现原理和HashMap相同功能相同。底层都使用哈希表结构查询速度快很多情况下可以互用。与HashMap不同Hashtable不允许使用null 作为key 和value与HashMap一样Hashtable也不能保证其中Key-Value 对的顺序Hashtable判断两个key相等、两个value相等的标准与HashMap一致。
6.13、Properties处理属性文件
Properties 类是Hashtable的子类该对象用于处理属性文件由于属性文件里的key、value 都是字符串类型所以Properties 里的key 和value 都是字符串类型存取数据时建议使用setProperty(String key,Stringvalue)方法和getProperty(String key)方法 1、新建jdbc.properties文件 2、编写源代码 import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;public class PropertiesTest {//Properties:常用来处理配置文件。key和value都是String类型public static void main(String[] args){//快捷键ALTShiftZFileInputStream fis null;try {Properties pros new Properties();fis new FileInputStream(jdbc.properties);pros.load(fis); //加载流对应文件String name pros.getProperty(name);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();}}}}
} 如果jdbc.properties文件中写入为中文 防止jdbc.properties出现中文乱码可根据如下解决 07、Collections工具类
操作数组的工具类ArraysCollections 是一个操作Set、List和Map 等集合的工具类Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作还提供了对集合对象设置不可变、对集合对象实现同步控制等方法排序操作均为static方法 reverse(List)反转List 中元素的顺序shuffle(List)对List集合元素进行随机排序sort(List)根据元素的自然顺序对指定List 集合元素按升序排序sort(ListComparator)根据指定的Comparator 产生的顺序对List 集合元素进行排序swap(Listintint)将指定list 集合中的i处元素和j 处元素进行交换
7.1、Collections工具类常用方法的测试
import org.junit.Test;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;/*** Collections:操作Collection、Map的工具类** 面试题Collection 和 Collections的区别* Collection是集合类的上级接口继承于他的接口主要有Set 和List.* Collections是针对集合类的一个帮助类他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作.*/
public class CollectionTest {/*** reverse(List)反转 List 中元素的顺序* shuffle(List)对 List 集合元素进行随机排序* sort(List)根据元素的自然顺序对指定 List 集合元素按升序排序* sort(ListComparator)根据指定的 Comparator 产生的顺序对 List 集合元素进行排序* swap(Listint int)将指定 list 集合中的 i 处元素和 j 处元素进行交换** Object max(Collection)根据元素的自然顺序返回给定集合中的最大元素* Object max(CollectionComparator)根据 Comparator 指定的顺序返回给定集合中的最大元素* Object min(Collection)* Object min(CollectionComparator)* int frequency(CollectionObject)返回指定集合中指定元素的出现次数* void copy(List dest,List src)将src中的内容复制到dest中* boolean replaceAll(List listObject oldValObject newVal)使用新值替换 List 对象的所有旧值**/Testpublic void test(){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);// Collections.reverse(list);
// Collections.shuffle(list);
// Collections.sort(list);
// Collections.swap(list,1,2);int frequency Collections.frequency(list, 123);System.out.println(list);System.out.println(frequency);}Testpublic void test2(){List list new ArrayList();list.add(123);list.add(43);list.add(765);list.add(-97);list.add(0);//报异常IndexOutOfBoundsException(Source does not fit in dest)
// List dest new ArrayList();
// Collections.copy(dest,list);//正确的List dest Arrays.asList(new Object[list.size()]);System.out.println(dest.size());//list.size();Collections.copy(dest,list);System.out.println(dest);/*** Collections 类中提供了多个 synchronizedXxx() 方法* 该方法可使将指定集合包装成线程同步的集合从而可以解决* 多线程并发访问集合时的线程安全问题*///返回的list1即为线程安全的ListList list1 Collections.synchronizedList(list);}
}