【Java专题】Java集合框架详解

 

什么是集合?集合有什么作用?

其实数组就是一个集合,集合实际上就是一个容器,可以用来容纳其他类型的数据。
 

集合装的是什么?

集合中不能直接存储基本类型的数据,也不能直接存储java对象,他存储的是java对象的内存地址。
 
注意: 集合本身是一个对象,有自己的内存地址,集合中任何时候存储的都是引用
 

不同的集合对应的底层到底怎么样?

java中每一个不同的集合,底层会对应不用的数据结构,往不同的集合中存储元素,等于将数据放到不同的数据结构中。
 

集合在JDK中的哪个包下?

所有的集合类和集合接口都在java.util包下   java.util.*;
 

java集合的分类:

一、单个方式存储元素,超级父接口:java.util.Collection;
二、以键值对的方式存储的元素,超级父接口:java.util.Map;
 

java集合继承结构图:

 
【Java专题】Java集合框架详解_第1张图片
 

集合框架总体图:

【Java专题】Java集合框架详解_第2张图片

 

集合详解:

Collection
1、Collection中能存什么元素?
Collection中可以存object的所有子类型,另外他也只能存储java对象的内存地址
 
2、 Collection中常用的方法:
  • boolean add(Object e) 向集合中添加元素
  • int size()  获取集合中元素的个数
  • void clear() 清空集合
  • boolean contains(Object o) 判断当前集合中是否包含元素o,包含返回true,不包含返回false
  • boolean remove(Object o) 删除集合中的某个元素。
  • boolean isEmpty()  判断该集合中元素的个数是否为0
  • Object[] toArray()  调用这个方法可以把集合转换成数组
 
  3、常用方法实践
/**
* @author Jason
* @create 2020-07-07 9:45
* 集合常用方法
*/
public class CollectionTest01 {
  public static void main(String[] args) {
    System.out.println("学习集合常用方法");
    Collection collection = new ArrayList();
    //下面是自动装箱,其实效果和collection.add(2);是一样的
    Integer a = new Integer(2);
    collection.add(a);

    //往集合中添加元素
    collection.add(1);
    collection.add("java");
    collection.add(new Object());
    collection.add(3.14);
    collection.add(true);

    //判断集合中元素的个数
    System.out.println("collection集合的大小:"+collection.size());
    System.out.println(collection);

    //删除集合中的某个元素
    collection.remove("java");
    System.out.println(collection);

    //判断当前集合中是否包含元素o
    boolean flag = collection.contains(2);
    System.out.println(flag);

    //判断集合中元素是否为0
    System.out.println(collection.isEmpty());

    //将集合转换成数组
    Object[] objs = collection.toArray();
    for (int i = 0; i < objs.length; i++) {
      Object o = objs[i];
      System.out.println(o);
    }

    //清空集合
    collection.clear();
    System.out.println(collection.size());
  }
}

 

 
4、集合的遍历
//集合的遍历
//第一步:获取迭代器对象
Iterator iterator = collection.iterator();
//第二步:使用迭代器对象遍历集合
while (iterator.hasNext()) {
  System.out.println(iterator.next());
}

 

 
注意: 存进去的是什么类型,取出来的是什么类型,但是输出的时候会自动转换成字符串,因为println()会调用toString()方法。HashSet无序不可重复,ArrayList有序可重复。
 
5、深入contains()方法
/**
* @author Jason
* @create 2020-07-07 10:59
*/
public class CollectionTest03 {
  public static void main(String[] args) {
    Collection c = new ArrayList();
    String str1 = new String("a");
    c.add(str1);

    String str2 = new String("b");
    c.add(str2);

    String str3 = new String("c");
    c.add(str3);

    System.out.println("集合中元素的个数:" + c.size());

    //创建一个新的对象
    String s = new String("a");
    //判断c集合中是否包含s引用所指向的对象 true
    System.out.println(c.contains(s));
  }
}

 

判断集合中是否包含s所指向的对象,是如何判断的呢?
请看源码:(进入ArrayList()源码,搜索contains(),查看其调用的方法indexOf(),很容易就发现,contains底层调用了equal方法进行比较两个对象)
   
 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}
其实上面实例的代码,跟下面实例的原理相同:
/**
* @author Jason
* @create 2020-07-07 21:35
* contains底层探索
*/
public class CollectionTest04 {
  public static void main(String[] args) {
    Collection collection = new ArrayList();
    User user1 = new User("jason");
    collection.add(user1);
    User user2 = new User("jason");
    //false 原因在于user没有重写equals,调用的是object类的equals方法
    // 他们比较的是对象的内存地址
    // object底层的equals方法
    //public boolean equals(Object obj) {
    //   return (this == obj);
    //}
    //不重写equals方法的时候,返回的是false,如果重写了equals方法则返回true
    System.out.println(collection.contains(user2));
  }
}


class User{
  private String name;
  public User(){}


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


  //重写equals方法(只要姓名一样就表示同一个用户)他就会按照重写的equals方法进行比较
  public boolean equals(Object o) {
    if (o==null || !(o instanceof User)) {
      return false;
    }
    if (o == this) {
      return true;
    }
    User u = (User)o;
    return u.name.equals(this.name);
  }
}

 

结论: 存放在一个集合中的类型,一定要重写equals方法(重写了比较的是内容,不重写比较的是内存地址)
 
在看一个实例:
public class CollectionTest05 {
  public static void main(String[] args) {
    Collection collection = new ArrayList();

    Integer x = new Integer(1000);
    collection.add(x);

    Integer y = new Integer(1000);
    System.out.println(collection.contains(y));
  }
}

 

控制台输出:
true
分析:因为在实例化了Integer的时候,Integer底层就已经重写了equals方法,底层代码如下:
public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

 

6、深入remove方法
/**
* @author Jason
* @create 2020-07-07 21:35
* remove方法底层探索
*/
public class CollectionTest06 {
  public static void main(String[] args) {
    //创建集合
    Collection c = new ArrayList();
    //创建字符串对象
    String s1 = new String("jason");
    c.add(s1);

    String s2 = new String("jason");
    c.remove(s2);

    System.out.println(c.size());
  }
}

 

 
分析: 上面的代码remove方法后,返回的是什么?当然是0.那么它为什么会是0呢?这个又回到了最初讨论的问题,那就是他们的底层到底干了什么,String类的底层是重写了equals方法,才可能比较的是内容,否则比较的是内存地址,众所周知,引用类型变量的内存地址永远不可能相等。也就是说,如果不重写equals方法,是不可能remove掉的。
 
下面是String类底层重写的equals方法:
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

 

下面我们看一个不重写equals方法的remove情况
/**
* @author Jason
* @create 2020-07-07 21:35
* contains底层探索
*/
public class CollectionTest04 {
  public static void main(String[] args) {
    Collection collection = new ArrayList();
    User user1 = new User("jason");
    collection.add(user1);
    User user2 = new User("jason");
    collection.remove(user2);
    System.out.println("没有重写equals方法的remove:"+collection.size());
  }
}

class User{
  private String name;
  public User(){}
  public User(String name) {
    this.name = name;
  }

  //重写equals方法(只要姓名一样就表示同一个用户)
  /*public boolean equals(Object o) {
    if (o==null || !(o instanceof User)) {
      return false;
    }
    if (o == this) {
      return true;
    }
    User u = (User)o;
    return u.name.equals(this.name);
  }*/
}

 

控制台输出结果:
没有重写equals方法的remove:1
 
注意: 当我们的集合结构发生变化时,迭代器必须重新获取,如果还是以前老的迭代器会出现异常:java.util.ConcurrentModificationException.原因:在于集合中获取的迭代器对象是用来遍历集合的。此时相当于对当前集合的状态拍了一个快照,迭代器迭代的就是这个快照内容,当集合发生变化了,相当于之前的快照就不是变化后集合的快照了。
 
具体实例代码:
/**
* @author Jason
* @create 2020-07-08 16:31
*/
public class CollectionTest05 {
  public static void main(String[] args) {
    //创建集合
    Collection c = new ArrayList();
    //添加数据
    c.add(1);
    c.add(2);
    c.add(3);
    c.add(4);
    //创建迭代器
    Iterator it = c.iterator();
    while (it.hasNext()) {
      Object o = it.next();
      //集合删除元素,没有通知迭代器,迭代器不知道集合发生了变化。
      c.remove(o);
      //it.remove();
      System.out.println(o);
    }
    System.out.println("删除后集合大小:"+c.size());
  }
}

 

集合删除会报异常: 集合删除元素,没有通知迭代器,迭代器不知道集合发生了变化。
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at CollectionTest05.main(CollectionTest05.java:21)

 

但是用迭代器直接删除就没有问题:原因: 迭代器删除,会自动更新迭代器,并且更新集合,输出结果如下:
1
2
3
4

 

删除后集合大小:0
 
注意: 在删除集合元素的时候,一定要用Iterator的remove方法删除元素,不要直接用集合自身的remove方法删除元素。
 
 
 
 
 
 
 
 
 
 
 
 

你可能感兴趣的:(●Java,java集合详解,集合中contains方法,java集合继承结构图,java集合框架图,java集合底层详解)