集合框架
/** * 一、集合框架的概述 * 1. 集合、数组都是对多个数据进行存储操作的结构,简称Java容器。 * 说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储 * 2. 数组在存储多个数据方面的特点: * > 一旦数组初始化后,其长度就确定了 * > 数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。 * 比如:String[] arr; int[] arr1; Object[] arr2; * 缺点:>一旦初始化以后,其长度就不可修改。 * > 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。 * > 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用。 * > 数组存取数据的特点:有序、可重复. 对于无序、不可重复的需求,不能满足。 * 二、集合框架 * |---Collection 接口:单列集合,用来存储一个一个的对象 * |----List接口 :存储有序的、可重复的数据 * |---- ArrayList、LinkedList、Vector (实现类) * * |----Set接口 :存储无序的、不可重复的数据 * |-----HashSet、LinkedHashSet、TreeSet(实现类) * * |---Map接口:双列结合,用来存储一对一对(key - value)的数据 * |-----HashMap、LinkedHashMap、TreeMap、HashTable、Properties(实现类) * 三、Collection接口中的方法的使用 * * * @author miaotiaojun * @create 2020-08-21 12:04 */public class CollectionTest { @Test public 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("DD"); coll1.add("pp"); coll.addAll(coll1); System.out.println(coll); //[AA, BB, 123, Fri Aug 21 22:25:40 CST 2020, DD, pp] // isEmpty() : 判断当前集合是否为空 System.out.println(coll.isEmpty()); //false // clear():清空集合元素 coll.clear(); System.out.println(coll); } @Test public void test2(){ // contains(Object obj):判断当前集合中是否包含obj 调用的 对象的equals() 方法 // String类重写了 equals // 2.containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中 // 3.remove(Object obj): 返回一个boolean值 // 4.removeAll(Collection coll1) : 从当前集合中移除coll1中的所有的元素 // 5.retainAll(Collection coll1): 交集,获取当前集合和coll1集合的交集,并返回给当前集合 // 6.equals(Collection coll1): 比较的是两个集合的元素 的 equals()方法。 // 7.hashCode():返回当前对象的哈希值 // 8.toArray(): 把集合 转成数组 。 // 拓展:数组--->集合 Arrays.asList(new String[]{"AA","BB","CC"}); // iterator():返回Iterator接口的实例,用于遍历集合元素。 }}public class IteratorTest { /* > 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。 > 如果需要创建Iterator对象,则必须有一个被迭代的集合. > Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法, 那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象 > 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用 remove 都会报 IllegalStateException。 */ @Test public void test1(){ Collection coll = new ArrayList(); coll.add(123); coll.add(345); coll.add("miaotiao"); coll.add(false); Iterator iterator = coll.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } /* jdk 5.0 新增了foreach 循环,用于遍历集合、数组 */ @Test public void test(){ Collection coll = new ArrayList(); coll.add(1234); coll.add(454); coll.add(new String("Tom")); coll.add(false); // for ( 集合元素的类型 局部变量 :集合对象) for (Object obj : coll){ System.out.println(obj); } }} /* 面试题:ArrayList、LinkedList、Vector三者的异同? 同:三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据 不同点:ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用 Object[] elementData 存储 LinkedList:对于频繁的插入、删除操作,使用此类效率比 ArrayList高;底层使用双向链表存储 Vector:作为List接口给古老接口实现类;线程安全的,效率低;底层使用Object[] elementData存储 */ /* 常用方法: 增:add(Object obj) 删:remove(int index) / remove(Object obj) 改:set(int index,Object ele) 查:get(int index) 插:add(int index,Object ele) 长度:size() 遍历:1. Iterator迭代器方式 2.增强for循环 3.普通的循环 */ @Test public void test(){ ArrayList list = new ArrayList(); list.add(123); list.add(345); 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 (int i = 0; i < list.size(); i++){ System.out.println(list.get(i)); }
Set接口
/* Set接口:存储无序的、不可重复的数据 |-----HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值 |-----LinkedSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序取 |-----TreeSet:可以按照添加对象的指定属性,进行排序。 1.无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值来添加 2.不可重复性:保证添加的元素按照equals()判断时,不能返回true,即相同的元素只能添加一个 二、添加元素的过程:以HashSet为例: 我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet 底层数组中的存放位置(即 索引位置),判断数组此位置上是否已经有元素: 如果此位置上没有其他元素,则此元素a添加成功。 如果此位置上有其他元素b(或以链表形式存在多个元素),则比较元素a与元素b的hash值: 如果哈希值不相同,则元素a 添加成功 如果哈希值相同,进而需要调用元素a所在类的 equals()方法: equals()返回true,元素a添加失败 equals()返回false,则元素a添加成功。 jdk 7 :元素a放在数组中,指向原来的元素 jak 8 ;原来的元素在数组中,指向元素a 1.Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法 2.要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals() LinkedHashSet 的使用:对于频繁的遍历操作,LinkedHashSet效率高于HashSet TreeSet :1. 不能添加不同类的 对象; 2. 两种排序方式:实现Comparable接口 ; 实现Comparator 接口 3. 判断能否插入到 TreeSet 调用的是 对象的 comparteTo()方法 */
Map接口
/* |----Map:双列数据,存储key-value对的数据 |----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) 二、Map结构的理解: Map种的Key:无序的、不可重复的,使用Set存储所有的key Map中的value:无序的、可重复的,使用Collection存储所有的value 一个键值对:key-value构成了一个Entry对象 Map中的entry:无序的、不可重复的,使用Set存储所有的entry 三、HashMap的底层实现原理?jdk7为例 HashMap map = new HashMap(); 在实例化以后,底层创建了长度是16的一维数组 Entry[] tabel; map.put(key1,value1); 首先,调用key1所在类的hashCode()计算key1的哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。 如果此位置上的数据为空,此时的key1-value1添加成功 如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值: 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。 如果key1的哈希值和已经存在的某一个数据的哈希值相同,继续比较:调用key1所在类的equals()方法,比较: 如果返回值为false:此时key1-value1添加成功。 如果返回true:此时 value1替换相同key的value值 (put方法 有修改功能) jdk8 相较于jdk7在底层实现方面的不同: 1. new HashMap():底层没有创建一个长度为16的数组 2. jdk8底层的数组是Node[],而非Entry[] 3. 首次调用put()方法时,底层创建长度为16的数组 4. jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时, 此时索引位置上的所有数据改为使用红黑树存储。 */ /* map中常用方法 1. Object put(Object o1,object o2): 2. putAll(Map m): 3. remove(Object key): 移除指定的key的 key-value键值对,并返回value 4. void clear(): 清空当前map中的所有数据。 5. get(Object key):获取指定key对应的value 6. boolean containsKey(Object key):是否包含指定的Key 7. boolean containsValue(Object value):是否包含指定的value 8. int size(): 返回map中key-value对的个数 9. boolean isEmpty(): 判断当前map是否为空 10. boolean equals(Object obj):判断当前map和参数对象obj是否相等 比较的是map的数据 元视图操作的方法:遍历map集合 11. Set keySet(): 返回所有key构成的Set集合 12. Collection values(): 返回所有value构成的Collection集合 13. Set entrySet(): 返回所有 key-value对构成的Set集合 entry.getKey(), entry.getValue() */ /* TreeMap: 添加key-value,要求key必须是由一个类创建的对象 因为要按照key 进行排序:自然排序、定制排序。 */ /* Collections:操作Collection、Map的工具类 */ @Test public void test1(){ List list = new ArrayList(); list.add(123); list.add(34); list.add(567); // 复制操作 List des = Arrays.asList(new Object[list.size()]); System.out.println(des.size());// 3 Collections.copy(des,list); System.out.println(des); /* Collections 类中提供了多个 synchronizedXxx() 方法, 该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题 */ // 返回的list即为线程安全的List List list1 = Collections.synchronizedList(list); }
泛型
在泛型类中的 静态方法中不能使用类的方法
异常类不能声明为泛型的
泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有
任何关系
换句话说:泛型方法所属的类是不是泛型类都没有关系。
4. 泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定 的,并非在实例化类时确定。
@Testpublic <E> List<E> copyFromArrayToList(E[] arr){ ArrayList<E> list = new ArrayList<>(); for(E e : arr){ list.add(e); } return list;} /* 2. 通配符的使用 通配符: ? 类A是类B的父类,G<A>和G<B>是没有关系的,二者的父类是:G> 添加(写入):对于List>就不能向其内部添加数据。除了添加null之外。 获取(读取):允许读取数据,读取的数据类型为Object. 3. 有限制条件的统配符的使用。 G< ? extends Person >: Person 及 Person的子类 G super Person >: Person 及 Person的父类 */