集合的简述

集合

一、集合框架的概述

  1. 集合、数组都是对多个数据进行存储操作的结构,简称Java容器。
    说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中存储)

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

  • —旦初始化以后,其长度就确定了。

  • 数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。比如:String[] arr;int[] arr1;object[] arr2;

  1. 数组在存储多个数据方面的缺点:

    • 一旦初始化以后,其长度就不可修改。

    • 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便
      同时效率不高。

    • 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用

    • 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

二、集合框架

Collection接口:单列集合,用来存储一个一个的对象

List接口:存储有序的,可重复的数据—>“动态数组”

ArrayList、LinkedList、Vector

Set接口:存储无序的,不可重复的数据

HashSet、LinkedHashSet(HashSet的子类)、TreeSet

Map接口:双列集合,用来存储一堆(key-value)一对的数据

HashMap、LinkedHashMap(子类)、TreeMap、Hashtable、Properties(子类)

框架图解

Collection接口:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T6Py8T7I-1626345535937)(file://C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210712111819542.png?lastModify=1626060294)]

Map接口:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fEZbSmnX-1626345535940)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210712111836569.png)]

三、Collection子接口—List接口

向List接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().

List接口:存储有序的、可重复的数据

3.1 常用方法

@Test
public void test5(){
    Collection coll=new ArrayList();
    Collection coll1=new ArrayList();

    //add(Object e):将元素e添加到集合coll中
    coll.add("aa");
    coll.add(123);
    coll.add(new Date());
    System.out.println(coll.size());//3
    //addAll(Collection coll)将coll集合中的元素添加到当前集合中
    coll1.add(456);//自动装箱
    coll.addAll(coll1);

    System.out.println(coll.toString());
    System.out.println(coll.size());//4

    coll.clear();//不是null,只是清空里面的元素

    //isEmpty():判断当前集合是否为空,即size是否为0
    System.out.println(coll.isEmpty());
}

@Test
    public void test() {
        //contains(Object obj):判断当前集合中是否包含obj
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add("ss");
        coll.add("java");
        coll.add(new String("java"));
        coll.add(new Person("Jay", 30));

        //在判断时会调用obj对象所在类的equals()
        boolean contains = coll.contains(123);
        System.out.println(contains);

        System.out.println(coll.contains(new String("java")));//true
        System.out.println(coll.contains(new Person("Jay", 30)));//false,没有重写equals方法

        //containsAll(Collection coll):判断coll中的所有元素是否在当前集合中

        Collection coll1 = Arrays.asList(123, "java");
        System.out.println(coll.containsAll(coll1));//true
    }

    @Test
    public void test1() {
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add("ss");
        coll.add("java");
        coll.add(new String("java"));
        coll.add(new Person("Jay", 30));

        //remove(Object obj):移除obj元素,返回布尔值
        boolean remove = coll.remove(123);
        System.out.println(remove);//true
        System.out.println(coll);

        //removeAll(Collection coll1):从当前集合中移除coll1中所有元素(差集)
        Collection coll1 = Arrays.asList(123, "ss", "java");
        coll.removeAll(coll1);
        System.out.println(coll);
    }

    @Test
    public void test2() {
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add("ss");
        coll.add("java");
        coll.add(new String("java"));
        coll.add(new Person("Jay", 30));

        Collection coll1 = new ArrayList();
        coll1.add(123);
        coll1.add("ss");
        coll1.add("java");
        coll1.add(new String("java"));
        coll1.add(new Person("Jay", 30));

        //equals():当前集合与形参集合是否相同,如果是ArraysList需要考虑顺序
        System.out.println(coll1.equals(coll));//true

        //hashCode():返回当前对象的哈希值
        System.out.println(coll.hashCode());

        //retainAll(Collection coll1);求交集,返回个当前集合
        Collection coll2 = Arrays.asList(123, 456, 789);
        coll.retainAll(coll2);
        System.out.println(coll);
    }

3.2 与index有关的常用方法

@Test
public void test(){
    ArrayList list=new ArrayList(10);
    list.add(123);
    list.add(new String("666"));
    list.add(new Person("Jay",30));
    System.out.println(list);

    //add(int index,Object obj):指定位置添加元素
    list.add(1,"JDBC");
    System.out.println(list);

    //addAll(int index,Collection coll):从指定位置开始,将coll中的所有元素添加到当前集合中
    List list1= Arrays.asList("888",4396);
    list.addAll(0,list1);
    System.out.println(list);

    //Object get(int index):获取指定位置的元素
    System.out.println(list.get(2));
}
@Test
public void test1(){
    ArrayList list=new ArrayList(10);
    list.add(123);
    list.add(new String("666"));
    list.add(new Person("Jay",30));

    //int indexOf(Object obj):返回obj首次在集合中出现的位置,如果不存在,返回-1
    System.out.println(list.indexOf(123));//0
    System.out.println(list.indexOf(122));//-1

    //int lastIndexOf(Object obj):返回obj最后一次出现在集合中的位置,不存在返回-1
    System.out.println(list.lastIndexOf(new Person("Jay",30)));//2,Person类中重写过equals()

    //remove(int index)
    Object o=list.remove(0);
    System.out.println(o);//被删除的元素
    System.out.println(list);

    //Object set(int index,Object obj):将指定位置上的元素设置为obj
    Object set = list.set(0, 777);
    System.out.println(set);//被修改的元素
    System.out.println(list);

    //System.out.println(list.set(2,56));//报错,index不能超出list.size()

    //List subList(int fromIndex,int toIndex):返回从fromList到toList位置的左闭右开区间的子集合
    list.add(55);
    list.add(2,13);
    List list1 = list.subList(0, 2);
    System.out.println(list);
    System.out.println(list1);

}

3.3 集合与数组的转化

@Test
public void test3(){
    Collection coll = new ArrayList();
    coll.add(123);
    coll.add("ss");
    coll.add("java");
    coll.add(new String("java"));
    coll.add(new Person("Jay", 30));

    //集合--->数组
    Object[] array = coll.toArray();
    for (int i = 0; i <array.length ; i++) {
        System.out.println(array[i]);
    }

    //数组--->集合
    List list = Arrays.asList("123", 123, "java");
    List integer=Arrays.aslist(new int[]{11,22});
    System.out.println(integer.size());//1
    List integers = Arrays.asList(new Integer[]{11, 22});
    System.out.println(integers.size());//2
    System.out.println(list);
    System.out.println(integers);
}

3.4 集合的遍历

集合元素的遍历操作,使用迭代器Iterator接口

  1. 内部的方法:hasNext()和next()
  2. 集合对象每次调用iterator()方法,都会得到一个全新的迭代器对象,默认游标都在集合的第一个元素.
@Test
    public void test(){
        //集合元素的遍历操作,使用迭代器Iterator接口
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add("ss");
        coll.add("java");
        coll.add(new String("java"));
        coll.add(new Person("Jay", 30));

        Iterator iterator=coll.iterator();

        //不推荐
//        for (int i = 0; i <coll.size() ; i++) {
//            System.out.println(iterator.next());
//        }

        //推荐使用
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        //错误的遍历方式一:会跳着输出
//        while (iterator.next()!=null){
//            System.out.println(iterator.next());
//        }
        
        //错误的遍历方式二:会重复死循环输出第一个元素
//        while (coll.iterator().hasNext()){
//            System.out.println(coll.iterator().next());//死循环
//        }
        
    }
@Test//增强for循环
public void test(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add("ss");
        coll.add("java");
        coll.add(new String("java"));
        coll.add(new Person("Jay", 30));

        //内部任然调用迭代器
        for (Object obj:coll){
            System.out.println(obj);
        }
    }
    @Test//遍历数组
    public void test1(){
        int [] arr=new int[]{1,5,7};
        for (int i:arr){
            System.out.println(i);
        }
    }

3.5 ArrayList源码理解

3.5.1 jdk 7情况下
  • ArrayList list = new ArrayList(); //底层创建了长度是10的object[]数组eLementData

  • list.add(123); //eLementData[e] = new Integer(123);

  • 如果此次的添加导致底层eLementData数组容量不够,则扩容。默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。

  • 结论:建议开发中使用带参的构造器:

    ArrayList list = new ArrayList(int capacity)

3.5.2 jdk 8中Arraylist的变化:
  • ArrayList list = new ArrayList();//底层object[] eLementData初始化为{}.并没有创建长list.add(123);

  • 第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到eLemen…
    后续的添加和扩容操作与jdk7无异。

  • 小结: jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。

3.6 List接口实现类的比较

ArrayList、LinkedList、Vector三者的异同?

同:三个类都实现了List接口,存储数据的特点相同:存储有序的、可重复的数据

异:

  • ArrayList:List接口的主要实现类,线程是不安全的,但是效率高,底层使用Object[] elementData存储
  • LinkedList:底层使用双向链表存储,对于频繁的插入、删除操作,使用LinedList比ArrayList效率高
  • Vector:线程安全,效率低,底层是Object[] elementData

四、Collection子接口—Set接口

4.1 Set接口实现类比较

set接口:存储无序的、不可重复的数据

无序性:不等于随机性。存储的数据在底层数组中并非按照数组的索引顺序添加,而是根据数据的哈希值决定

不可重复性:保证添加的元素按照equals()方法添加时,不能返回true,即相同的元素只能添加一个

set接口没有额外定义新的方法,使用的都是Collection中声明过的方法。

  • Hashset:作为set接口的主要实现类:线程不安全的,可以储存null值

  • LinkHashSet:作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历

  • TreeSet:可以按照添加对象的指定属性,进行排序,向TreeSet中添加数据,要求是相同类的对象。

    • 自然排序中,比较两个对象是否相同的标准为:compareTo返回0,不再是equals()

4.2 添加元素的方式

  • 向set接口的实现类添加数据时,要求其所在类一定要重写hashCode()和equals()
  • 要保证重写的hashCode()和equals()尽可能保持一致:相等的对象必须有相同的散列码

我们向HashSet中添加元素a,首先调用元素α所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:

  • 如果此位置上没有其他元素,则元素a添加成功。—>情况1
    如果此位置上有其他元素(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
  • 如果hash值不相同,则元素α添加成功。—>情况2
    如果hash值相同,进而需要调用元素α所在类的equlas()方法:
  • equals()返回true,元素a添加失败
    equals()返回false,则元素α添加成功。—>情况3

对于添加成功的情况2和情况3而言:元素与已经存在指定索引位置上数据以链表的方式存储。

jdk 7:元素a放到数组中,指向原来的元素。

jdk 8:原来的元素在数组中,指向元素α

总结:七上八下

HashSet底层:数组+链表的结构。

4.3 一道关于set的经典题目

体会set添加元素的过程

//Perosn已经重写equals()和hashCode()
@Test
public void test4(){
    HashSet set=new HashSet();
    Person p1= new Person("aa",20);
    Person p2= new Person("bb",30);

    set.add(p1);
    set.add(p2);
    System.out.println(set);//[Person{name='aa', age=20}, Person{name='bb', age=30}]

    p1.setName("cc");
    set.remove(p1);
    System.out.println(set);//[Person{name='cc', age=20}, Person{name='bb', age=30}]

    set.add(new Person("cc",20));
    System.out.println(set);//[Person{name='cc', age=20}, Person{name='cc', age=20}, Person{name='bb', age=30}]

    set.add(new Person("aa",20));
    System.out.println(set);//[Person{name='cc', age=20}, Person{name='cc', age=20}, Person{name='aa', age=20}, Person{name='bb', age=30}]
    
}

五、Map接口

Map接口:双列数据,存储key-value对的数据

  • HashMap:主要实现类,线程不安全,效率高,能存储null的key和value

    • LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历
      • 原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。
  • TreeMap:可以按照添加的key-value对进行排序,实现排序遍历,此时考虑key的自然排序和定制排序

    ​ 底层使用的是红黑树

  • Hashtable:古老实现类,线程安全,效率低,不能存储null的key和value

    • Properties:常用来处理配置文件,key和value都是String类型

HashMap底层:数组+链表(jdk7之前)

​ 数组+链表+红黑树(jdk8)

5.1 Map结构的理解

Map中的key:无序的,不可重复的,使用Set存储所有的key

  • key所在的类要重写equals()和HashMap()

Map中的value:无序的,可重复的,使用List存储所有的value

  • value所在的类要重写equals()

一个键值对:key-value构成一个Entry对象

Map中的Entry:无序的,不可重复的,使用Set存储所有的Entry

5.2 HashMap常用方法

@Test
public void test(){
    HashMap hashMap=new HashMap();
    //put()
    hashMap.put("123",10);
    hashMap.put("java",777);
    //修改
    hashMap.put("java",4396);
    System.out.println(hashMap);
    //putAll()
    HashMap hashMap1=new HashMap();
    hashMap1.put("kass",2200);
    hashMap1.put("zed",123);
    hashMap.putAll(hashMap1);
    System.out.println(hashMap);
    //remove(Object key):移除指定key的键值对
    System.out.println(hashMap.remove("123"));
    System.out.println(hashMap);
    //clear():清空数据,不是置为null
    hashMap1.clear();
    System.out.println(hashMap1.size());
}
@Test
public void test1(){
    HashMap hashMap=new HashMap();
    hashMap.put("123",10);
    hashMap.put("java",777);
    hashMap.put("java",4396);
    //get(Object key)
    Object o = hashMap.get("123");
    System.out.println(o);
    //containsKey(Object key):是否包含指定的key
    boolean containsKey = hashMap.containsKey("java");
    System.out.println(containsKey);
    //containsKey(Object value):是否包含指定的value
    boolean containsValue = hashMap.containsValue("777");
    System.out.println(containsValue);

    //isEmpty()
    HashMap hashMap1=new HashMap();
    hashMap1.put("aa",123);
    System.out.println(hashMap1.isEmpty());//false
    hashMap1.clear();
    System.out.println(hashMap1.isEmpty());//true
}
@Test
public void test2(){
    //Set keySet():返回所有key构成的Set集合
    HashMap hashMap=new HashMap();
    hashMap.put("123",10);
    hashMap.put("java",777);
    hashMap.put("java",4396);
    Set set = hashMap.keySet();
    Collection values = hashMap.values();
    Iterator iterator = set.iterator();
    Iterator iterator1 = values.iterator();
    while (iterator.hasNext()&&iterator1.hasNext()){
        System.out.print(iterator.next()+"=");
        System.out.print(iterator1.next());
        System.out.println();
    }

    Set entrySet = hashMap.entrySet();
    for (Object obj:entrySet){
        System.out.println(obj);
    }
    //Collection values():返回所有value构成的Collection集合
    //Set entrySet():返回所有key-value对构成的Set集合
}

5.3 HashMap的底层原理

JDK7
HashMap map = new HashMap( ):

在实例化以后,底层创建了长度是16的一维数组Entry[ ] table

map.put( key1, value1):

​ 首先,调用key1所在类的nashCode()计算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的改变
  1. new hashMap():底层没有创建一个长度为16的数组
  2. jdk 8底层的数组是 Node[] ,而非Entry[]
  3. 首次调用put方法时,底层创建长度为16的数组
  4. jdk7底层只有数组+链表,jdk8中加入了红黑树
  5. 当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64时,此时此索引位置上的所有数据改为使用红黑树存储

5.4 TreeMap

@Test
public void test3(){
    //向treeMap中添加key-value,要求key必须是同一个类创建的对象
    //因为要按照key排序:自然排序,定制排序
    TreeMap treeMap=new TreeMap();
    treeMap.put(new Person("jj",20),80);
    treeMap.put(new Person("aa",20),70);
    treeMap.put(new Person("cc",20),60);
    treeMap.put(new Person("gg",20),50);
    treeMap.put(new Person("kk",20),40);

    Set entrySet = treeMap.entrySet();
    for (Object obj:entrySet){
        System.out.println(obj);
    }
}

六、Collections工具类

Collections:操作Collection、Map的工具类

6.1 常用方法

@Test
public void test(){
    List list=new ArrayList();
    list.add(123);
    list.add(456);
    list.add(456);
    list.add(789);
    //reverse(List list):反转list
    System.out.println(list);
    Collections.reverse(list);
    System.out.println(list);
    //shuffle(List list):集合元素进行随机排序
    Collections.shuffle(list);
    System.out.println(list);
    //sort(List list):自然排序
    Collections.sort(list);
    System.out.println(list);
    //swap(List list,int i,int j):将i与j索引位置的元素交换
    Collections.swap(list,0,1);
    System.out.println(list);
    //frequency(Collection,Object):返回指定集合中指定元素的出现次数
    int frequency = Collections.frequency(list, 456);
    System.out.println(frequency);
}
@Test
public void test1(){
    List list=new ArrayList();
    list.add(123);
    list.add(456);
    list.add(456);
    List dest= Arrays.asList(new Object[list.size()]);
    //将list中的元素复制到dest中。
    Collections.copy(dest,list);
    System.out.println(dest);
}

6.2 线程问题

Collections 类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

@Test
public void test(){
    List list=new ArrayList();
    list.add(123);
    list.add(456);
    list.add(456);
    list.add(789);
    //reverse(List list):反转list
    System.out.println(list);
    Collections.reverse(list);
    System.out.println(list);
    //shuffle(List list):集合元素进行随机排序
    Collections.shuffle(list);
    System.out.println(list);
    //sort(List list):自然排序
    Collections.sort(list);
    System.out.println(list);
    //swap(List list,int i,int j):将i与j索引位置的元素交换
    Collections.swap(list,0,1);
    System.out.println(list);
    //frequency(Collection,Object):返回指定集合中指定元素的出现次数
    int frequency = Collections.frequency(list, 456);
    System.out.println(frequency);
}
@Test
public void test1(){
    List list=new ArrayList();
    list.add(123);
    list.add(456);
    list.add(456);
    List dest= Arrays.asList(new Object[list.size()]);
    //将list中的元素复制到dest中。
    Collections.copy(dest,list);
    System.out.println(dest);
}

6.2 线程问题

Collections 类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

@Test
public void test2(){
    //线程问题
    List list=new ArrayList();
    list.add(123);
    list.add(456);
    List list1 = Collections.synchronizedList(list);
    //list1是线程安全的集合
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值