Java集合框架1--Collection接口

Java集合框架概述

  • Java集合就像一种容器,可以动态把多个对象的引用放入容器中

  • 集合框架的概述

    • 1、集合、数组都是对多个数据进行存储操作(内存层面)的结构,简称Java容器
    • 2.1、数组在存储多个数据方面的特点:
      •  一旦初始化之后,其长度就确定了
        
      •  数组一旦定义好,其元素的类型也就确定了。只能操作指定类型的数据。比如:`String[] arr`  ` int[] arr`
        
    • 2.2、数组在存储多个数据方面的缺点:
      •  一旦初始化之后,其长度就确定了
        
      •  数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不方便,同时效率也不高
        
      •  获取数组中实现元素的个数的需求,数组没有现成的属性或方法可用
        
      •  数组存储数据的特点:有序、可重复;对于无序、不可重复的需求,不能满足
        

集合框架涉及到的API

  • Java集合可分为Collection和Map两种体系

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

    • List接口:有序的、可重复的数据 —>"动态"数组
      • ArrayList、LinkedList、Vector
    • Set接口:无序的、不可重复的数据 —>类似于高中讲的"集合"
      • HashSet
      • LinkedHashSet
      • TreeSet
  • Map接口:双列集合,用来存储一对一对(Key-Value)的数据 —>函数 y = f ( x ) y = f(x) y=f(x)

    • HashMap
    • LinkedHashMap
    • TreeMap
    • HashTable
    • Properties

Collection接口中的常用方法1

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());

        //addAll(Collection c):将c中集合的元素添加到当前集合中
        Collection coll1 = new ArrayList();
        coll1.add(456);
        coll1.add("CC");
        coll.addAll(coll1);
        System.out.println(coll.size());//6
        System.out.println(coll);

        //isEmpty():判断当前集合是否为空
        System.out.println(coll.isEmpty());//false

        //clear():清空集合元素
        coll.clear();//并不是把coll置为空指针,而是把数据清空了
        coll.isEmpty();//true
    }

}

Collection接口中的常用方法2

public class CollectionTest {
    @Test
    public void test1(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add("A");
        coll.add(new String("CCC"));
        coll.add(false);
        coll.add(new Person("Tom",20));

        //contains(Object obj):判断当前集合是否包含obj
        //在判断时会调用obj对象所在类的equals()方法
        //向Collection接口的实现类中添加数据obj时,要求obj所在类要重写equals()方法
        boolean con = coll.contains("A");//true
        System.out.println(con);
        System.out.println(coll.contains(new String("CCC")));//true (调用的是equals)
        System.out.println(coll.contains(new Person("Tom",20)));//true,但若没有重写equals方法则为false

        //containsAll(Collection c):判断c中的所有元素是否都存在于当前集合中
        Collection coll1 = Arrays.asList(123,456);//返回的是List接口
        System.out.println(coll.containsAll(coll1));//false
    }

}

class Person{
    private String name;
    private int age;
    public Person(String name,int age) {
        this.age = age;
        this.name = name;
    }

    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;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }
}

Collection接口中的常用方法3

@Test
public void test(){
    Collection coll = new ArrayList();
    coll.add(123);
    coll.add("A");
    coll.add(new String("CCC"));
    coll.add(false);

    //remove(Object obj):从该集合中删除指定元素的单个实例(如果存在),删除成功返回true,失败返回false
    //如果要删除自定义类,则需要重写equals()方法
    coll.remove(123);
    System.out.println(coll);

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

@Test
public void test2(){
    Collection coll = new ArrayList();
    coll.add(123);
    coll.add("A");
    coll.add(new String("CCC"));
    coll.add(false);

    //retainAll():仅保留此集合中包含在指定集合中的元素,返回值为boolean.
    Collection coll1 = Arrays.asList(123,456,789);
    coll.retainAll(coll1);
    System.out.println(coll); //[123]

    //equals(Object obj):将指定的对象与此集合进行比较以获得相等性 要注意List的有序性 返回值为boolean
    System.out.println(coll.equals(Arrays.asList(123)));  //true
}

Collection接口中的常用方法4

public class CollectionTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add("A");
        coll.add(new String("CCC"));
        coll.add(false);

        //hashCode():返回此集合的哈希值
        System.out.println(coll.hashCode());

        //toArray():返回一个包含此集合中所有元素的数组Object[]
        Object[] arr = coll.toArray();
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        //拓展:数组-->集合:调用Arrays的静态方法asList()
        List<String> list = Arrays.asList(new String[]{"AA","bb","cc"});
        System.out.println(list);

        List list1 = Arrays.asList(new int[]{123,456});
        System.out.println(list1);//[[I@4ee285c6]
        System.out.println(list1.size());//1

        List list2 = Arrays.asList(new Integer[]{123,456});
        System.out.println(list2.size());//2

        //iterator():返回此集合中的元素的迭代器(Iterator接口的实例),用于遍历集合元素
    }
}

使用Iterator遍历Collection

/**
 * 集合元素的遍历操作,使用迭代器Iterator接口
 * hasNext()  next()
 *
 */
public class IteratorTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add("A");
        coll.add(new String("CCC"));
        coll.add(false);

        //方式1:不推荐
        Iterator iterator = coll.iterator();
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        //异常:java.util.NoSuchElementException
//        System.out.println(iterator.next());
//
//        //方式2:不推荐
//        for (int i=0;i<coll.size();i++){
//            System.out.println(iterator.next());
//        }

        //方式3
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }
}

迭代器Iterator的执行原理

在这里插入图片描述

Iterator遍历集合的两种错误

@Test
public void test01(){
    Collection coll = new ArrayList();
    coll.add(123);
    coll.add("A");
    coll.add(new String("CCC"));
    coll.add(false);
    Iterator iterator = coll.iterator();

    //错误方式一:指针已经改变
    while (iterator.next()!=null){    //java.util.NoSuchElementException
        System.out.println(iterator.next());
    }

    //错误方式二  死循环(iterator()每次都生成新的迭代器对象,默认游标都在第一个元素之前
    while (coll.iterator().hasNext()){
        System.out.println(coll.iterator().next());
    }
}

Iterator迭代器remove()的使用

/**
 * 迭代器Iterator的remove():从底层集合中删除此迭代器返回的最后一个元素
 * 此方法不同于集合直接调用remove
 * 异常
 * UnsupportedOperationException -如果 remove操作不会被这个迭代器支持
 * IllegalStateException - 如果 next方法尚未被调用,或者 remove方法在上次调用 next方法之后已经被调用
 */
public class IteratorTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add("A");
        coll.add(new String("CCC"));
        coll.add(false);
        Iterator iterator = coll.iterator();

        while (iterator.hasNext()){
            Object obj = iterator.next();
            if ("CCC".equals(obj)){
                iterator.remove();
            }
        }
        //再次遍历集合
        Iterator iterator1 = coll.iterator();
        while (iterator1.hasNext()){
            System.out.println(iterator1.next());
        }
    }
}

新特性foreach循环遍历集合或项目

@Test
public void test(){
    Collection coll = new ArrayList();
    coll.add(123);
    coll.add("A");
    coll.add(new String("CCC"));
    coll.add(false);

    //for (集合元素类型 局部变量:集合对象)
    //底层还是Iterator
    for (Object obj:coll){
        System.out.println(obj);
    }
}

List接口常用实现类的对比

List接口:有序的、可重复的数据 —>"动态"数组

  • ArrayList、LinkedList、Vector
    • 相同点:三者都实现了List接口,并且都存储有序的、可重复的数据
    • 但是ArrayList是List接口的主要实现类,线程不安全,执行效率高,底层使用Object[]存储
    • Vector是List接口的古老实现类,线程安全,执行效率低,底层使用Object[]存储
    • LinkedList对于频繁的插入和删除操作,使用LinkedList效率较高,因为底层使用双向链表存储。

ArrayList的源码分析

  • JDK7和JDK8情况下稍有不同

  • JDK7情况下

    ArrayList list = new ArrayList();//底层创建了一个长度是10的Object[]数组elementData
    list.add(123);  //elementData[0] = 123;
    ...
    list.add(11);//如果此次的添加导致底层elementData数组容量不够则扩容。默认情况下扩容为原来的1.5倍,同时将原有数组的数据复制到新数组中
    
    //结论:开发中建议使用带参的构造器ArrayList list = new ArrayList(int capacity);
    
  • JDK8情况下

    transient Object[] elementData; // non-private to simplify nested class access
    
    
    /**
         * Constructs an empty list with an initial capacity of ten.
         */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    ArrayList list = new ArrayList();//底层Object[]数组elementData初始化为{},并没有创建长度为10的数组
    list.add(123);  //底层创建长度为10的数组,并将123添加到第一个位置上,后续添加与扩容操作与JDK7相同。
    ...
    list.add(11);
    
  • 小结:JDK7中的ArrayList对象创建类似于单例模式中的饿汉式

    ​ JDK8中的ArrayList对象创建类似于单例模式中的懒汉式,延迟了对象创建,节省内存。

LinkedList的源码分析

/**
 * Links e as last element.
 */
void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}


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;
    }
}
@Test
public void test(){
    LinkedList list = new LinkedList();//内部声明了Node类型的first和last属性,默认值为null
    list.add(123);//将123封装到Node,创建Node对象
}

Vector的源码分析

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}
  • JDK7和JKD8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组

    在扩容方面,默认扩容为原来数组长度的2倍

List接口中常用方法的测试

  • 重点:CRUD、遍历
public class ListTest {
    @Test
    public void test(){
        ArrayList list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add("A");
        list.add(false);
        System.out.println(list);

        //add(int index, E element)  将指定的元素插入此列表中的指定位置
        list.add(1,"ABC");
        System.out.println(list);

        //addAll(int index, Collection<? extends E> c)  从index位置开始将c中所有元素添加到当前List
        List list1 = Arrays.asList(1,2,3);
        list.addAll(2,list1);
        System.out.println(list.size());

        //indexOf(Object o) 返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1
        System.out.println(list.indexOf(123));

        //int lastIndexOf(Object o)
        //返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。

        //E remove(int index) 删除该列表中指定位置的元素
        //boolean remove(Object o) 从列表中删除指定元素的第一个出现(如果存在)
        //boolean removeAll(Collection<?> c) 从此列表中删除包含在指定集合中的所有元素
        list.remove(2);//默认index
        list.remove(new Integer(123));
        list.remove("A");

        //E set(int index, E element) b用指定的元素替换此列表中指定位置的元素
        list.set(0,999);

        //List<E> subList(int fromIndex, int toIndex) 返回此列表中指定的 fromIndex (含)和 toIndex之间的视图
        //(左闭右开)
        List list2 = list.subList(2,4);//list本身不改变
    }
}

Set接口实现类的对比

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

    • HashSet:作为Set接口的主要实现类;县城不安全,可以存储null值
    • LinkedHashSet:extends HashSet,作为HashSet的子类,遍历其内部数据时可以按照添加的顺序遍历
    • TreeSet:可以按照添加对象的指定属性进行排序
  • Set接口是Collection的子接口,set接口没有提供额外的方法

    • Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个

    ​ Set 集合中,则添加操作失败。

    • Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法

Set的无序性和不可重复性

Set:存储无序的、不可重复的数据

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

以HashSet为例说明:

  1. 无序性:不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值添加
  2. 不可重复性:保证添加的元素按照equals()方法判断时,不能返回true,即相同的元素只能添加一次。

HashSet中元素的添加过程

HashSet底层:数组+链表

以HashSet为例

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

  1. 如果此位置没有其他元素,则元素a直接添加成功;

  2. 如果此位置上有其他元素b(或已经存在以链表形式的多个元素),则比较元素a和元素b的hash值,如果hash值不相同,则元素a添加成功;

  3. 如果hash值相同,进而需要调用元素a所在类的equals()方法,equals()返回true,元素a添加失败,返回false则添加成功。

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

jdk7:元素a放到数组上,指向原来的元素

jdk8:原来的元素在数组中,指向元素a

总结:七上八下(康师傅太秀了)

关于hashCode()和equals()的重写

在这里插入图片描述

要求:向Set中添加的数据,其所在类一定要重写equals()和hashCode()

要求:重写的equals()和hashCode()尽可能保持一致性 相等的对象必须具有相等的散列码

在这里插入图片描述

结论:复写equals方法的时候一般都需要同时复写hashCode方法。通

常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。

LinkedHashSet的使用

  • LinkedHashSet 是 HashSet 的子类

  • LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,

    但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入

    顺序保存的。

  • LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全

    部元素时有很好的性能。

  • LinkedHashSet 不允许集合元素重复。

  • 涉及频繁的遍历操作时,比HashSet效率要高

在这里插入图片描述

TreeSet的自然排序

  1. 向TreeSet中添加的数据,要求是相同类的对象,否则报异常java.lang.ClassCastException
  2. 两种排序方式:自然排序(实现comparable接口)和定制排序(实现Comparator接口)
  • 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,而不再是equals()
public class User implements Comparable{
    private int age;
    private String name;

    public User(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //按照姓名从小到大排列,若姓名相同则按年龄从小到大
    @Override
    public 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("输入的类型不匹配");
        }
    }
}
@Test
public void  test(){
    TreeSet set = new TreeSet();
    set.add(new User(15,"Tom"));
    set.add(new User(20,"Davis"));
    set.add(new User(18,"James"));
    set.add(new User(32,"Rose"));
    set.add(new User(35,"Iverson"));
    set.add(new User(34,"Iverson"));

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

TreeSet的定制排序

  • 自然排序中,比较两个对象是否相同的标准为:compare()返回0,而不再是equals()
@Test
public void test2(){
    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(15,"Tom"));
    set.add(new User(20,"Davis"));
    set.add(new User(18,"James"));
    set.add(new User(32,"Rose"));
    set.add(new User(35,"Iverson"));

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值