Java高级编程–集合框架–day04、05

1.Java集合框架概述

Java容器:集合、数组都是对多个数据进行存储操作的结构
Java 集合就像一种容器,可以动态地把多个对象的引用放入容器中。

2.数组在存储多个数据方面的特点:

1.一旦初始化以后,其长度就确定了
2.数组一旦定义好,其元素的类型也就确定了
3.提供的操作非常有限,对添加、删除、插入数据等操作,非常不便
4.获取数组中实际元素的个数的需求,数组没有现成的属性或方法使用
5.数组存储数据的特点:有序、可重复,对于无序、不可重复的需求,不能满足。

3.Java集合可分为那两个体系

Collection和Map
Collection接口:单列数据,定义了存取一组对象的方法的集合
1.List:元素有序、可重复的集合
ArrayList、LinkedList、Vector
2.Set:元素无序、不可重复的集合
HashSet、LinkedHashSet、TreeSet
在这里插入图片描述
Map接口:双列数据,保存具有映射关系“key-value对”的集合
HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
在这里插入图片描述

4.Collection接口中声明的方法的测试

向Collection接口的实现类的对象中添加数据obj时

5.Collection接口中的方法

1.boolean add(E e)

 Collection coll=new ArrayList();
 coll.add("AA");
    coll.add("BB");
    coll.add(123);
    coll.add(new Date());

2.boolean addAll(Collection<? extends E> c)

Collection coll1=new ArrayList();
coll1.add(456);
coll1.add("CC");
coll.addAll(coll1);

3.void clear()

coll.clear();

4.boolean contains(Object o)
//使用加入对象的equals方法进行比较

boolean contains=coll.contains(123);

5.boolean containsAll(Collection<?> c)

Collection coll2= Arrays.asList(123,456);
System.out.println(coll2.containsAll(coll2));

6.boolean equals(Object o)//比较两个集合中的元素,根据集合自身的特性决定是否考虑先后顺序
7.int hashCode()
8.boolean isEmpty()//是否有数据
System.out.println(coll.isEmpty());
9.Iterator iterator()
10.boolean remove(Object o)

coll.remove(123); 

11.boolean removeAll(Collection<?> c)//移除两个集合交集数据

coll.removeAll(coll2);

12.boolean retainAll(Collection<?> c)//获取两个集合的交集,删除非交集

Collection coll3=Arrays.asList(123,456,789);
coll.retainAll(coll3);

13.int size()
14.集合转化为数组

Object[] toArray() 
Object[] arr=coll.toArray();

15. T[] toArray(T[] a)
数组–>集合

List<String> list=Arrays.asList(new String[]{"AA","BB","CC"});//ArrayList
List arr1=Arrays.asList(new int[]{123,456});
System.out.println(arr1.size());//1
List arr2=Arrays.asList(new Integer[]{123,456});
System.out.println(arr2.size());//2
6.Iterator迭代器接口

1.Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
2.
Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了
Iterator接口的对象。
3.Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建Iterator 对象,则必须有一个被迭代的集合。
4.集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。
Iterator遍历越界会报如下的错误:NoSuchElementException
样例测试方法:size()和hasNext()

public void test2(){
    Collection col1=new ArrayList();
    col1.add(123);
    col1.add(456);
    col1.add(new String("Tom"));
    col1.add(false);
    Iterator iterator=col1.iterator();
    //for(int i=0;i<col1.size();i++){
    //    System.out.println(iterator.next());
    //}
    while(iterator.hasNext()){
        System.out.println(iterator.next());
    }
}

测试Iterator中的remove()

public void test3(){
    Collection col1=new ArrayList();
    col1.add(123);
    col1.add(456);
    col1.add(new String("Tom"));
    col1.add(false);
    Iterator iterator=col1.iterator();
    while(iterator.hasNext()){
        Object obj=iterator.next();
        if("Tom".equals(obj)){
            iterator.remove();
        }
    }
    while(iterator.hasNext()){
        System.out.println(iterator.next());
    }
}

注意:如果还未调用next()或在上一次调用next方法之后已经调用了remove方法,再调用remove都会报IllegalStateException

7.1.5之后新增了foreach循环
    public void test4(){
        Collection col1=new ArrayList();
        col1.add(123);
        col1.add(456);
        col1.add(new String("Tom"));
        col1.add(false);
        Iterator iterator=col1.iterator();
  //内部仍然调用了迭代器
        for(Object obj:col1){
            System.out.println(obj);
        }
    }
8.ArrayList、LinkedList、Vector三者的异同?

相同点:都实现了List接口,存储有序的、可重复的数据
不同地点:
ArrayList:作为List接口的主要实现类:线程不安全的,效率高;底层使用Object[]存储;
LinkedList:底层使用的双向链表存储,对于频繁的插入、删除操作,使用此类效率比ArrayList高;
Vector:作为List接口的古老实现类:线程安全的,效率低;底层使用Object[]存储;初始长度为10,两倍扩容
关于扩容:
1.ArrayList jdk1.7的情况
ArrayList list=new ArrayList();//底层创建长度是10的Object[]数组,扩容按1.5倍,如果不够以实际长度进行扩容,如果实际长度超过某一个临界值,将报错,建议开发中使用带参的构造器:ArrayList list=new ArrayList(int capacity);
ArrayList jdk1.8的情况
ArrayList list=new ArrayList();//底层Object[] elementData初始化为{},并没有创建数组,第一次调用add()时,底层才创建长度为10的数组,后续和jdk7类似
ArrayList jdk1.13的情况
ArrayList list=new ArrayList();//底层创建长度是0的Object[]数组,后续处理和jdk8相似
2.LinkedList的源码分析
LinkedList list=new LinkedList();//内部声明了Node类型的first和last属性,默认值为null,add的时候处理链表的添加
Node的定义为:
private static class Node {
E item;
LinkedList.Node next;
LinkedList.Node prev;
Node(LinkedList.Node prev, E element, LinkedList.Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}

9.List接口中涉及到的方法

void add(int index, Object ele):在index位置插入ele元素
boolean addAll(int index, Collection eles):从index(从0开始计算)位置开始将eles中的所有元素添加进来
Object get(int index):获取指定index位置的元素
int indexOf(Object obj):返回obj在集合中首次出现的位置,如果不存在返回-1
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位置的左闭右开的子集合

public void test5(){
    ArrayList list=new ArrayList();
    list.add(123);
    list.add(456);
    list.add("AA");
    list.add(456);
    list.add("BB");
    System.out.println(list);
    List list1=Arrays.asList(1,2,3);
    list.addAll(list1);
    int index=list.indexOf(4567);
    System.out.println(index);
    System.out.println(list.lastIndexOf(456));
    Object obj=list.remove(0);
    list.set(1,"CC");
    List subList=list.subList(2,4);
    System.out.println(subList);
}
10.List中常用的方法

增: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循环
③普通的循环

11.remove的代码示例
private void updateList(List list){
        list.remove(2);//按索引删除数据
        //list.remove(new Integer(2));
    }
    @Test
    public void test6(){
        List list=new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        updateList(list);
        System.out.println(list);
    }
12.Set接口的框架

Collection接口:单列集合,用来存储一个一个的对象
----Set接口:存储无序的(不等于随机性,根据hashcode进行存储)、不可重复的数据(保证添加的元素按照equals()判断时,不能返回为true,即:相同的元素只能添加一个)
--------HashSet:作为Set接口的主要实现类,线程不安全的,可以存储null值
------------LinkedHashSet:作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历
--------TreeSet:可以按照添加对象的指定属性,进行排序

13.HashSet添加元素的过程

jdk7:创建数组有默认长度,为16
jdk8:一开始不进行初始化,调用add的时候进行初始化
添加的过程:
1.先计算hashCode,底层根据hashCode计算位置,如果位置上不存在数据,则直接放;
2.如果计算位置上存在对象,则依次比较hashCode是否相等,如果相等,再比较equals,如果不等添加。
3.如果位置相同,但是hashCode不等,则添加元素。
jdk7.0是把新元素直接放到数组上,然后指向原来的链表结构
jdk8.0是把新元素放到链表的末尾
要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals(),并且两者的返回值尽量保持一致:相等的对象必须具有相等的散列码

14.以Eclipse/IDEA为例,在自定义类中可以调用工具自动重写equals和hashCode。问题:为什么用Eclipse/IDEA复写hashCode方法,有31这个数字?

1.选择系数的时候要选择尽量大的系数。因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。(减少冲突)
2.并且31只占用5bits,相乘造成数据溢出的概率较小。
3.31可以 由i*31== (i<<5)-1来表示,现在很多虚拟机里面都有做相关优化。(提高算法效率)
4.31是一个素数,素数作用就是如果我用一个数字来乘以这个素数,那么最终出来的结
果只能被素数本身和被乘数还有1来整除!(减少冲突)

15.LinkedHashSet

1.LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护两个引用,记录此数据的前一个数据和后一个数据。
2.优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet

16.TreeSet

1.向TreeSet中添加的数据,要求是相同类的对象
2.两种排序方式:自然排序(实现Comparable接口)和定制排序(Comparator)
3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()
需要添加的类型实现Comparable接口
4.定制排序(Comparator),比较两个对象是否相同的标准为:compare()返回0,不再是equals()

public void test8(){
    Comparator com=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("输入的数据类型不匹配");
            }
        }
    };
    TreeSet set=new TreeSet(com);
    set.add(new User("Tom",12));
    set.add(new User("Tom",32));
    set.add(new User("Tom",2));
    System.out.println(set);
}

5.二叉平衡树\红黑树存储结构

17.集合Colleciton中存储的如果是自定义类的对象,需要自定义类重写那个方法?为什么?

List:equals()方法,查找、删除对象的时候会用到
Set:(HashSet、LinkedHashSet为例):equals()、hashCode()
TreeSet:Comparable:compareTo(Object obj)
Comparator:compare(Object o1,Object o2)

18.查看下面代码的运行结果
public void test9(){
    HashSet set = new HashSet();
    User p1 = new User(1001,"AA");
    User p2 = new User(1002,"BB");
    set.add(p1);
    set.add(p2);
    p1.setName("CC");
    set.remove(p1);
    System.out.println(set);
    set.add(new User(1001,"CC"));
    System.out.println(set);
    set.add(new User(1001,"AA"));
    System.out.println(set);
}

重写了equals方法和hashCode方法
结果:
[User{name=‘BB’, age=1002}, User{name=‘CC’, age=1001}]
[User{name=‘BB’, age=1002}, User{name=‘CC’, age=1001}, User{name=‘CC’, age=1001}]
[User{name=‘BB’, age=1002}, User{name=‘CC’, age=1001}, User{name=‘CC’, age=1001}, User{name=‘AA’, age=1001}]

19.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)

20.Map结构的理解

1.Map中的key:无序的、不可重复的,使用Set存储所有的key—>key所在的类要重写equals()和hashCode()(以HashMap为例)
2.Map中的value:无序的、可重复的,使用Collection存储所有的value—>value所在的类要重写equals()
3.一个键值对:key-value构成了一个Entry对象
4.Map中的entry:无序的、不可重复的,使用Set存储所有的entry

21.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倍,并将原有的数据复制过来。       

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时,此时此索引位置上的所数据改为使用红黑树存储。

22.加载因子、扩容的临界值

  DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
   DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
   threshold:扩容的临界值,=容量*填充因子:16 * 0.75 => 12
   TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
   MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64 
23.LinkedHashMap的底层实现原理

节点采用双向表形式记录添加元素的先后顺序

24.Map中定义的方法

1.添加、删除、修改操作:

Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
 public void test10(){
        Map map=new HashMap();
        map.put("AA",123);
        map.put(45,123);
        map.put("BB",56);
        //修改
        map.put("AA",87);
        System.out.println(map);
    }

void putAll(Map m):将m中的所有key-value对存放到当前map中
Object remove(Object key):移除指定key的key-value对,并返回value
void clear():清空当前map中的所有数据
2.元素查询的操作:
 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是否相等
3.元视图操作的方法:
 Set keySet():返回所有key构成的Set集合

  Set set=map.keySet();
        Iterator iterator=set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

 Collection values():返回所有value构成的Collection集合

  Collection values=map.values();
        for(Object obj:values){
            System.out.println(obj);
        }

 Set entrySet():返回所有key-value对构成的Set集合

    Set entrySet=map.entrySet();
    Iterator iterator1=entrySet.iterator();
    while(iterator.hasNext()){
        Object obj=iterator1.next();
        Map.Entry entry=(Map.Entry)obj;
    }
 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);
    }
25.TreeMap

向TreeMap中添加key-value,要求key必须是由同一个类创建的对象,因为要按key进行排序:自然排序、定制排序

   TreeMap map=new TreeMap();
    User u1=new User(23,"Tom");
    User u2=new User(32,"Jerry");
    User u3=new User(20,"Jack");
    User u4=new User(18,"Rose");
    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();
        Map.Entry entry=(Map.Entry)obj;
        System.out.println(entry.getKey()+"---->"+entry.getValue());
    }
26.Properties

1.Properties 类是 Hashtable 的子类,该对象用于处理属性文件
2.由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key和 value 都是字符串类型
3.存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法

public static void main(String[] args)  {
    Properties pros=new Properties();
    FileInputStream fis = null;
    try {
        fis=new FileInputStream("jdbc.properties");
        pros.load(fis);
        System.out.println(pros.getProperty("name"));
        System.out.println(pros.getProperty("password"));
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try { 
            if(fis!=null)
            fis.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }
}
27.Collections:操作Collection、Map的工具类

1.排序操作:(均为static方法)
reverse(List):反转 List 中元素的顺序
shuffle(List):对 List 集合元素进行随机排序
sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
2.查找、替换
Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中

   public void test12(){
        List list=new ArrayList();
        list.add(123);
        list.add(43);
        list.add(765);
        list.add(-97);
        list.add(0);
        List dest=Arrays.asList(new Object[list.size()]);
        System.out.println(dest);
        Collections.copy(dest,list);
        System.out.println(dest);
    }

boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List 对象的所有旧值
3.Collections常用方法:同步控制
Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
在这里插入图片描述
样例代码://返回的list1即为线程安全的List
List list1=Collections.synchronizedList(list);
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值