集合概述


  • 概念:对象的容器,定义了对多个对象进项操作的的常用方法。可实现数组的功能。

  • 和数组的区别:

  1. 数组长度固定,集合长度不固定。

  2. 数组可以存储基本类型和引用类型,集合只能存储引用类型。

  • 位置: java.util.*;

 

Collection体系集合


Collection父接口


  • 特点:代表一组任意类型的对象,无序、无下标、不能重复。

  • 方法:

    • boolean add(Object obj) //添加一个对象。

    • boolean addAll(Collection c) //讲一个集合中的所有对象添加到此集合中。

    • void clear() //清空此集合中的所有对象。

    • boolean contains(Object o) //检查此集合中是否包含o对象。

    • boolean equals(Object o) //比较此集合是否与指定对象相等。

    • boolean isEmpty() //判断此集合是否为空。

    • boolean remove(Object o) //在此集合中移除o对象。

    • int size() //返回此集合中的元素个数。

    • Object[] toArray() //姜此集合转换成数组。

 1 /** 2  * Collection接口的使用(一) 3  * 1.添加元素 4  * 2.删除元素 5  * 3.遍历元素 6  * 4.判断 7  */ 8 public class Demo01 { 9     public static void main(String[] args) {10         //创建集合11         Collection collection = new ArrayList();12 13 //      * 1.添加元素14         collection.add("西瓜");15         collection.add("苹果");16         collection.add("榴莲");17         System.out.println(collection.size());18         System.out.println(collection.toString());19 20 //      * 2.删除元素21         collection.remove("西瓜");22         System.out.println("删除之后:"+collection.size());23 24 //      * 3.遍历元素25         //3.1 使用增强for26         for (Object object:collection) {27             String s = (String) object;28             System.out.println(s);29         }30         //3.2 使用迭代器(迭代器专门用来遍历集合的一种方式)31         //hasnext();判断是否有下一个元素32         //next();获取下一个元素33         //remove();删除当前元素34         Iterator it = collection.iterator();35         while (it.hasNext()){36             String s = (String) it.next();37             System.out.println(s);38             //删除操作39             //collection.remove(s);引发错误:并发修改异常40             //iterator.remove();应使用迭代器的方法41 42 //      * 4.判断43             System.out.println(collection.contains("苹果"));//true 是否存在苹果44             System.out.println(collection.isEmpty());//false 是否有数据,没有数据为true,有数据为false45         }46     }47 }
 1 /** 2  * Collection接口的使用(二) 3  * 1.添加元素 4  * 2.删除元素 5  * 3.遍历元素 6  * 4.判断 7  */ 8 public class Demo02 { 9     public static void main(String[] args) {10 11         //创建Collection对象12         Collection collection = new ArrayList();13 14         //创建Student对象15         Student s1 = new Student("张三",18);16         Student s2 = new Student("李四",19);17         Student s3 = new Student("王五",20);18 19         //1.添加数据20         collection.add(s1);21         collection.add(s2);22         collection.add(s3);23         //collection.add(s3);可重复添加相同对象24         System.out.println("元素个数为:"+collection.size());25         System.out.println(collection.toString());26 27         //2.删除数据28         collection.remove(s1);29         System.out.println("元素个数为:"+collection.size());30         System.out.println(collection.toString());31 32         //3.遍历数据33         //3.1 增强for循环34         for (Object object:collection) {35             Student s = (Student) object;36             System.out.println(object);37         }38 39         //3.2 迭代数据40         //迭代过程中不能使用Collection的删除方法,即Collection.remove()41         Iterator it = collection.iterator();42         while (it.hasNext()){43             Student s = (Student) it.next();44             System.out.println(s);45             //删除46             //it.remove();47         }48 49         //4判断50         System.out.println(collection.contains("张三"));//false51         System.out.println(collection.contains(s2));//true52         System.out.println(collection.isEmpty());//false53     }54 }
 1 /** 2  * 学生类 3  */ 4 public class Student { 5     private String name; 6     private int age; 7  8     //有参构造 9     public Student(String name, int age) {10         this.name = name;11         this.age = age;12     }13 14     //无参构造15     public Student() {16     }17 18     //重写toString19     @Override20     public String toString() {21         return "Student{" +22                 "name='" + name + '\'' +23                 ", age=" + age +24                 '}';25     }26 27     //get/set静态属性28     public String getName() {29         return name;30     }31 32     public void setName(String name) {33         this.name = name;34     }35 36     public int getAge() {37         return age;38     }39 40     public void setAge(int age) {41         this.age = age;42     }43 }

 

Collection子接口


List集合

  • 特点:有序、有下标、元素可以重复。

  • 方法:

    • void add(int index,Object o) //在index位置插入对象o。

    • boolean addAll(index,Collection c) //将一个集合中的元素添加到此集合中的index位置。

    • Object get(int index) //返回集合中指定位置的元素。

    • List subList(int fromIndex,int toIndex) //返回fromIndex和toIndex之间的集合元素。

 1 /** 2  * List子接口的使用(一) 3  * 特点:1.有序有下标 2.可以重复 4  * 5  * 1.添加元素 6  * 2.删除元素 7  * 3.遍历元素 8  * 4.判断 9  * 5.获取位置10  */11 public class Demo03 {12     public static void main(String[] args) {13 14         //1.创建list集合对象15         List list = new ArrayList<>();16 17         //2.添加元素18         list.add("迪迦");19         list.add("艾斯");20         list.add(0,"赛文");//根据index下标插入数据21         System.out.println("元素个数为:"+list.size());22         System.out.println(list.toString());23 24         //3.删除数据25         list.remove(1);26         //list.remove("迪迦");意思同上,一个是根据角标,一个是根据内容27         System.out.println("删除之后:"+list.size());28         System.out.println(list.toString());29 30         //4.遍历元素31         //4.1 因为有下标,所以可以使用for循环32         for (int i = 0; i < list.size(); i++) {33             System.out.println(list.get(i));34         }35         //4.2 使用增强for循环36         for (Object object:list) {37             String s = (String) object;38             System.out.println(s);39         }40         //4.3 使用迭代器41         Iterator it = list.iterator();42         while (it.hasNext()){43             String s = (String) it.next();44             System.out.println(s);45         }46         //4.4 使用列表迭代器47         ListIterator listIterator = list.listIterator();48         //从前往后遍历49         while (listIterator.hasNext()){50             String s = (String) listIterator.next();51             System.out.println(s);52         }53         //从后往前遍历(此时“遍历指针”已经指向末尾了)54         while (listIterator.hasPrevious()){55             String s = (String) listIterator.previous();56             System.out.println(s);57         }58 59         //5.判断60         System.out.println(list.contains("赛文"));//true61         System.out.println(list.isEmpty());//false62 63         //6.获取位置64         System.out.println(list.indexOf("艾斯"));//165     }66 }
 1 /** 2  * List子接口的使用(二) 3  * 1.添加元素 4  * 2.删除元素 5  * 3.遍历元素 6  * 4.判断 7  * 5.获取位置 8  */ 9 public class Demo04 {10     public static void main(String[] args) {11         List list = new ArrayList<>();12         //1.添加基本类型数字数据(自动装箱)13         list.add(10);14         list.add(20);15         list.add(30);16         list.add(40);17         list.add(40);18         System.out.println("元素个数为:"+list.size());19         System.out.println(list.toString());20 21         //2.删除数据22         list.remove(0);//根据下标删除23         //list.remove(40);下标越界24         list.remove(new Integer(40));//根据内容删除,将基本类型转成引用类型25         System.out.println("删除后:"+list.size());26         System.out.println(list.toString());27 28         //3.遍历元素29         //3.1 使用for循环30         for (int i = 0; i < list.size(); i++) {31             System.out.println(list.get(i));32         }33         //3.2 使用增强for循环34         for (Object object:list) {35             int s = (int) object;36             System.out.println(s);37         }38         //3.3 使用迭代器39         Iterator it = list.iterator();40         while (it.hasNext()){41             int s = (int) it.next();42             System.out.println(s);43         }44         //3.4 使用列表迭代器45         ListIterator listIterator = list.listIterator();46         //从前往后遍历47         while (listIterator.hasNext()){48             int s = (int) listIterator.next();49             System.out.println(s);50         }51         //从后往前遍历(此时“遍历指针”已经指向末尾了)52         while (listIterator.hasPrevious()){53             int s = (int) listIterator.previous();54             System.out.println(s);55         }56 57         //4.判断58         System.out.println(list.contains(20));//true59         System.out.println(list.isEmpty());//false60 61         //5.获取位置62         System.out.println(list.indexOf(30));//163 64         //6.补充方法sublist,根据角标范围,返回集合元素,含头不含尾65         List list1 = list.subList(0,2);66         System.out.println(list1.toString());//20,3067     }68 }

List实现类

ArrayList【重点】
  • 数组结构实现,查询快、增删慢;

  • JDK1.2版本,运行效率快、线程不安全。

 1 /** 2  * ArrayList的使用 3  * 存储结构:数组; 4  * 特点:查找遍历速度快,增删慢。 5  * 1.添加元素 6  * 2.删除元素 7  * 3.遍历元素 8  * 4.判断 9  * 5.查找10  */11 public class Demo05 {12     public static void main(String[] args) {13         ArrayList arrayList = new ArrayList<>();14         //1.添加元素15         Student s1 = new Student("钢铁侠",3);16         Student s2 = new Student("蜘蛛侠",4);17         Student s3 = new Student("蝙蝠侠",5);18         arrayList.add(s1);19         arrayList.add(s2);20         arrayList.add(s3);21         arrayList.add(s3);22         System.out.println("元素个数:"+arrayList.size());23         System.out.println(arrayList.toString());24 25         //2.删除元素26         arrayList.remove(s3);27         System.out.println("删除后:"+arrayList.size());28         System.out.println(arrayList.toString());29         //arrayList.remove(new Student("唐", 21));30         //注:这样可以删除吗(不可以)?显然这是两个不同的对象。31         //假如两个对象属性相同便认为其是同一对象,那么如何修改代码?32 33         //3.遍历元素34         //3.1使用迭代器35         Iterator it = arrayList.iterator();36         while (it.hasNext()){37             Student s = (Student) it.next();38             System.out.println(s);39         }40         //3.2使用列表迭代器41         ListIterator listIterator = arrayList.listIterator();42         //从前往后遍历43         while (listIterator.hasNext()){44             Student s = (Student) listIterator.next();45             System.out.println(s);46         }47         //从后往前遍历48         while (listIterator.hasPrevious()){49             Student s = (Student) listIterator.previous();50             System.out.println(s);51         }52 53         //4.判断54         System.out.println(arrayList.isEmpty());//false55         //System.out.println(arrayList.contains(new Student("钢铁侠", 3)));56         //注:与上文相同的问题。57 58         //5.查找59         System.out.println(arrayList.indexOf(s1));60     }61 }

注:Object里的equals(this==obj)用地址和当前对象比较,如果想实现代码中的问题,可以在学生类中重写equals方法:

 1     //重写equls方法 2     @Override 3     public boolean equals(Object obj) { 4         //1.是否为同一对象 5         if (this == obj){ 6             return true; 7         } 8         //2.判断是否为空 9         if (obj == null){10             return false;11         }12         //3.判断是否是Student类型13         if (this instanceof Student){14             Student s = (Student) obj;15             //4.比较属性16             if (this.name.equals(s.getName())&&this.age==s.age){17                 return true;18             }19         }20         return false;21     }
ArrayList源码分析:
  • 默认容量大小:private static final int DEFAULT_CAPACITY = 10;

  • 存放元素的数组:transient Object[] elementData;

  • 实际元素个数:private int size;

  • 创建对象时调用的无参构造函数:

1 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};2 //Arraylist无参构造方法3 public ArrayList() {4         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;5 }

这段源码说明当你没有向集合中添加任何元素时,集合容量为0。那么默认的10个容量怎么来的呢?这就得看看add方法的源码了:

1     public boolean add(E e) {2         ensureCapacityInternal(size + 1);  // Increments modCount!!3         elementData[size++] = e;4         return true;5     }

假设你new了一个数组,当前容量为0,size当然也为0。这时调用add方法进入到ensureCapacityInternal(size + 1);该方法源码如下:

1     private void ensureCapacityInternal(int minCapacity) {2         if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {3             minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);4         }5 6         ensureExplicitCapacity(minCapacity);7     }

由于一开始Arraylist的无参构造方法,就是将 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 赋值给了 elementData  ,所以if的条件是成立的。

if语句中 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 就是在 DEFAULT_CAPACITY 和 minCapacity 中返回一个最大的参数, DEFAULT_CAPACITY 是Arrarylist中一开始就定义的静态常量,大小为10。 minCapacity 传入的值为size+1也就是 1

DEFAULT_CAPACITY值大,因此返回DEFAULT_CAPACITY的值为10,这个值作为参数又传入ensureExplicitCapacity()方法中,进入该方法查看源码:

1     private void ensureExplicitCapacity(int minCapacity) {2         modCount++;3 4         // overflow-conscious code5         if (minCapacity - elementData.length > 0)6             grow(minCapacity);7     }

我们先不要管modCount这个变量,可以理解为这个变量是记录数组修改次数。

因为elementData数组长度为0,minCapacity为10,所以if条件成立,调用grow方法,重要的部分来了,这个代码就是数组扩容的代码,现在我们进入到grow方法的源码中:

 1     private void grow(int minCapacity) { 2         // overflow-conscious code 3         int oldCapacity = elementData.length;  //oldCapactity=0 4         int newCapacity = oldCapacity + (oldCapacity >> 1);  //oldCapacity=0;(oldCapacity >> 1)0右移一位还是0;因此newCapacity=0 5         if (newCapacity - minCapacity < 0)  //0-10=-10,if条件成立 6             newCapacity = minCapacity;    //newCapacity=10 7         if (newCapacity - MAX_ARRAY_SIZE > 0)  //MAX_ARRAY_SIZE这个值大家也可以进去看下,是好几个亿,这个条件基本上永远不会成立。可以忽略不看,了解即可 8             newCapacity = hugeCapacity(minCapacity); 9         // minCapacity is usually close to size, so this is a win:10         elementData = Arrays.copyOf(elementData, newCapacity);  //copyOf(elementData,newCapacity)创建一个新的数组elementDate,数组长度为newCapacity即就是1011     }

这个方法先声明了一个oldCapacity变量将数组长度赋给它,其值为0;又声明了一个newCapacity变量其值为oldCapacity+一个增量,可以发现这个增量是和原数组长度有关的量,当然在这里也为0。第一个if条件满足,newCapacity的值为10(这就是默认的容量,不理解的话再看看前面)。第二个if条件不成立,也可以不用注意,因为MAX_ARRAY_SIZE的定义如下:

这个值太大了以至于第二个if条件没有了解的必要。

最后一句话就是为elementData数组赋予了新的长度,Arrays.copyOf()方法返回的数组是新的数组对象,原数组对象不会改变,该拷贝不会影响原来的数组。copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值。

这时候再回到add的方法中,接着就向下执行elementData[size++] = e,将add传入的值e赋值给数组elementData,下标index为[size++]。到这里为止关于ArrayList就讲解得差不多了,当数组长度为10的时候你们可以试着过一下源码,查一下每次的增量是多少(答案是每次扩容为原来的1.5倍)。

 


 Vector
  • 数组结构实现,查询快、增删慢;

  • JDK1.0版本,运行效率慢、线程安全。

 1 /** 2  * Vector的演示使用 3  * 4  *1.添加数据 5  *2.删除数据 6  *3.遍历 7  *4.判断 8  */ 9 public class Demo01 {10     public static void main(String[] args) {11         //1.创建Vector对象12         Vector v = new Vector<>();13 14         //2.添加数据15         v.add("孙悟空");16         v.add("猪八戒");17         v.add("沙和尚");18         System.out.println("元素个数为:"+v.size());19         System.out.println(v.toString());20 21         //3.删除数据22         v.remove("沙和尚");23         //v.remove(2);效果同上24         System.out.println("删除后:"+v.size());25         System.out.println(v.toString());26 27         //4.遍历28         //使用枚举29         Enumeration elements = v.elements();30         while (elements.hasMoreElements()){31             System.out.println(elements.nextElement());32         }33 34         //5.判断35         System.out.println(v.isEmpty());36         System.out.println(v.contains("孙悟空"));37     }38 }

 


LinkedList
  • 链表结构实现,增删快,查询慢。

 1 /** 2  * LinkedList的用法 3  * 存储结构:双向链表 4  * 1.创建对象 5  * 2.添加元素 6  * 3.删除元素 7  * 4.遍历 8  * 5.判断 9  */10 public class Demo01 {11     public static void main(String[] args) {12         //1.创建LinkedList对象13         LinkedList link = new LinkedList<>();14 15         //2.添加元素16         Student s1 = new Student("python",1);17         Student s2 = new Student("Java",2);18         Student s3 = new Student("go",3);19         link.add(s1);20         link.add(s2);21         link.add(s3);22         link.add(s3);23         System.out.println("元素个数为:"+link.size());24         System.out.println(link.toString());25 26         //3.删除元素27         link.remove(s3);28         link.remove(new Student("go",3));29         System.out.println("删除后:"+link.size());30         System.out.println(link.toString());31 32         //4.遍历33         //4.1 使用for循环遍历34         for (int i = 0; i < link.size(); i++) {35             System.out.println(link.get(i));36         }37         //4.2 使用增强for38         for (Object object:link) {39             Student s = (Student) object;40             System.out.println(s.toString());41         }42         //4.3 使用迭代器43         Iterator it = link.iterator();44         while (it.hasNext()){45             System.out.println(it.next());46         }47         //4.4 使用列表迭代器48         ListIterator listIterator = link.listIterator();49         //从前往后遍历50         while (listIterator.hasNext()){51             System.out.println(listIterator.next());52         }53         //从后往前遍历54         while (listIterator.hasPrevious()){55             System.out.println(listIterator.previous());56         }57 58         //5.判断59         System.out.println(link.isEmpty());60         System.out.println(link.contains(s1));61         System.out.println(link.contains(new Student("Java",2)));62     }63 }

 

LinkedList源码分析

LinkedList首先有三个属性:

  • 链表大小:transient int size = 0;

  • (指向)第一个结点/头结点: transient Node<E> first;

  • (指向)最后一个结点/尾结点:transient Node<E> last;

关于Node类型我们再进入到类里看看:

 1     private static class Node<E> { 2         E item; 3         Node<E> next; 4         Node<E> prev; 5  6         Node(Node<E> prev, E element, Node<E> next) { 7             this.item = element; 8             this.next = next; 9             this.prev = prev;10         }11     }

首先item存放的是实际数据;next指向下一个结点而prev指向上一个结点。

Node带参构造方法的三个参数分别是前一个结点、存储的数据、后一个结点,调用这个构造方法时将它们赋值给当前对象。

LinkedList是如何添加元素的呢?先看看add方法:

1     public boolean add(E e) {2         linkLast(e);3         return true;4     }

进入到linkLast方法:

 1     void linkLast(E e) { 2         final Node<E> l = last; 3         final Node<E> newNode = new Node<>(l, e, null); 4         last = newNode; 5         if (l == null) 6             first = newNode; 7         else 8             l.next = newNode; 9         size++;10         modCount++;11     }

假设刚开始new了一个LinkedList对象,first和last属性都为空,调用add进入到linkLast方法。

首先创建一个Node变量 l 将last(此时为空)赋给它,然后new一个newNode变量存储数据,并且它的前驱指向l,后继指向null;再把last指向newNode。如下图所示:

如果满足if条件,说明这是添加的第一个结点,将first指向newNode:

至此,LinkedList对象的第一个数据添加完毕。假设需要再添加一个数据,我们可以再来走一遍,过程同上不再赘述,图示如下:


ArrayList和LinkedList区别
  • ArrayList:必须开辟连续空间,查询快,增删慢。

  • LinkedList:无需开辟连续空间,查询慢,增删快。


泛型概述

  • Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作为参数传递。

  • 常见形式有泛型类、泛型接口、泛型方法。

  • 语法:

    • <T,…> T称为类型占位符,表示一种引用类型。

  • 好处:

    • 提高代码的重用性。

    • 防止类型转换异常,提高代码的安全性。

泛型类
 1 /** 2  * 泛型类 3  * 语法:类名<T> 4  * T是类型占位符,表示一种引用类型,编写多个使用逗号隔开 5  * 6  */ 7 public class MyGeneric<T> { 8     //1.创建泛型变量 9     //不能使用new来创建,因为泛型是不确定的类型,也可能拥有私密的构造方法10     T t;11     //2.泛型作为方法的参数12     public void show(T t){13         System.out.println(t);14     }15 16     //3.泛型作为方法的返回值17     public T getT(){18         return t;19     }20 }
 1 /** 2  * 注意: 3  * 1.泛型只能使用引用类型 4  * 2.不同泛型类型的对象不能相互赋值 5  */ 6 public class TestGeneric { 7     public static void main(String[] args) { 8         //使用泛型类创建对象 9         MyGeneric<String> stringMyGeneric = new MyGeneric<>();10         stringMyGeneric.t = "葫芦娃";11         stringMyGeneric.show("火娃");12 13         MyGeneric<Integer> integerMyGeneric = new MyGeneric<>();14         integerMyGeneric.t=20;15         integerMyGeneric.show(30);16         Integer integer = integerMyGeneric.getT();17         System.out.println(integer);18     }19 }
泛型接口
 1 /** 2  * 泛型接口 3  * 语法:接口名<T> 4  * 注意:不能创建泛型静态常量 5  */ 6 public interface MyInterface<T> { 7     //创建常量 8     String nameString="金刚大芭比"; 9 10     //接口方法11     T server(T t);12 }
 1 /** 2  * 实现接口时确定泛型类 3  */ 4 public class MyInterfaceImpl implements MyInterface<String> { 5  6     @Override 7     public String server(String s) { 8         System.out.println(nameString); 9         System.out.println(s);10         return s;11     }12 }
1 //测试2 public class Application {3     public static void main(String[] args) {4         MyInterfaceImpl myInterface = new MyInterfaceImpl();5         myInterface.server("xxx");6     }7 }
 1 /** 2  * 实现接口时不确定泛型类 3  */ 4 public class MyInterfaceImpl2<T> implements MyInterface<T>{ 5     @Override 6     public T server(T t) { 7         System.out.println(nameString); 8         System.out.println(t); 9         return t;10     }11 }
1 //测试2 public class Application {3     public static void main(String[] args) {4         MyInterfaceImpl2<Integer> integerMyInterfaceImpl2 = new MyInterfaceImpl2<>();5         integerMyInterfaceImpl2.server(20);6     }7 }
泛型方法
1 /**2  * 泛型方法3  * 语法:<T> 返回类型4  */5 public class MyGenericMethod {6     public <T> void show(T t){7         System.out.println("泛型方法"+t);8     }9 }
1 //测试2 public class Application {3     public static void main(String[] args) {4         MyGenericMethod myGenericMethod = new MyGenericMethod();5         myGenericMethod.show("金刚");6         myGenericMethod.show(18);7     }8 }
泛型集合
  • 概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致。

  • 特点:

    • 编译时即可检查,而非运行时抛出异常。

    • 访问时,不必类型转换(拆箱)。

    • 不同泛型指尖引用不能相互赋值,泛型不存在多态。

之前我们在创建LinkedList类型对象的时候并没有使用泛型,但是进到它的源码中会发现:

1 public class LinkedList<E>2     extends AbstractSequentialList<E>3     implements List<E>, Deque<E>, Cloneable, java.io.Serializable4 {//略

它是一个泛型类,而我之前使用的时候并没有传递,说明java语法是允许的,这个时候传递的类型是Object类,虽然它是所有类的父类,可以存储任意的类型,但是在遍历、获取元素时需要原来的类型就要进行强制转换。这个时候就会出现一些问题,假如往链表里存储了许多不同类型的数据,在强转的时候就要判断每一个原来的类型,这样就很容易出现错误。


Set集合概述

Set子接口
  • 特点:无序、无下标、元素不可重复。

  • 方法:全部继承自Collection中的方法。

 1 /** 2  * 测试Set接口的使用 3  * 特点:1.无序,没有下标;2.重复 4  * 1.添加数据 5  * 2.删除数据 6  * 3.遍历【重点】 7  * 4.判断 8  */ 9 public class Demo01 {10     public static void main(String[] args) {11         Set<String> set = new HashSet<>();12         //1.添加数据13         set.add("火娃");14         set.add("金娃");15         set.add("木娃");16         set.add("水娃");17         //set.add("水娃");无法添加元素相同数据18         System.out.println("元素个数为:"+set.size());19         System.out.println(set.toString());20 21         //2.删除数据22         set.remove("水娃");23         System.out.println("删除后:"+set.size());24         System.out.println(set.toString());25 26         //3.遍历27         //3.1 使用增强for循环28         for (String s:set) {29             System.out.println(s);30         }31         //3.2 使用迭代器32         Iterator<String> iterator = set.iterator();33         while (iterator.hasNext()){34             System.out.println(iterator.next());35         }36 37         //4. 判断38         System.out.println(set.isEmpty());//false39         System.out.println(set.contains("火娃"));//true40     }41 }

Set实现类

HashSet【重点】
  • 基于HashCode计算元素存放位置。

  • 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入。

 1 /** 2  * 人类 3  */ 4 public class Person { 5     private String name; 6     private int age; 7  8     public Person(String name, int age) { 9         this.name = name;10         this.age = age;11     }12 13     public Person() {14     }15 16     public String getName() {17         return name;18     }19 20     public void setName(String name) {21         this.name = name;22     }23 24     public int getAge() {25         return age;26     }27 28     public void setAge(int age) {29         this.age = age;30     }31 32     @Override33     public String toString() {34         return "Person{" +35                 "name='" + name + '\'' +36                 ", age=" + age +37                 '}';38     }39 }
 1 /** 2  * HashSet集合的使用 3  * 存储结构:哈希表(数组+链表+红黑树) 4  * 1.添加元素 5  * 2.删除元素 6  * 3.遍历 7  * 4.判断 8  */ 9 public class Demo02 {10     public static void main(String[] args) {11         HashSet<Person> hashSet=new HashSet<>();12         Person p1=new Person("tang",21);13         Person p2=new Person("he", 22);14         Person p3=new Person("yu", 21);15         //1.添加元素16         hashSet.add(p1);17         hashSet.add(p2);18         hashSet.add(p3);19         //重复,添加失败20         hashSet.add(p3);21         //直接new一个相同属性的对象,依然会被添加,不难理解。22         //假如相同属性便认为是同一个对象,怎么修改?23         hashSet.add(new Person("yu", 21));24         System.out.println(hashSet.toString());25         //2.删除元素26         hashSet.remove(p2);27         //3.遍历28         //3.1 增强for29         for (Person person : hashSet) {30             System.out.println(person);31         }32         //3.2 迭代器33         Iterator<Person> iterator=hashSet.iterator();34         while (iterator.hasNext()) {35             System.out.println(iterator.next());36         }37         //4.判断38         System.out.println(hashSet.isEmpty());39         //直接new一个相同属性的对象结果输出是false,不难理解。40         //注:假如相同属性便认为是同一个对象,该怎么做?41         System.out.println(hashSet.contains(new Person("tang", 21)));42     }43 }

注:hashSet存储过程:

  1. 根据hashCode计算保存的位置,如果位置为空,则直接保存,否则执行第二步。

  2. 执行equals方法,如果方法返回true,则认为是重复,拒绝存储,否则形成链表。

存储过程实际上就是重复依据,要实现“注”里的问题,可以重写hashCode和equals代码:

 1 @Override 2 public int hashCode() { 3     final int prime = 31; 4     int result = 1; 5     result = prime * result + age; 6     result = prime * result + ((name == null) ? 0 : name.hashCode()); 7     return result; 8 } 9 @Override10 public boolean equals(Object obj) {11     if (this == obj)12         return true;13     if (obj == null)14         return false;15     if (getClass() != obj.getClass())16         return false;17     Person other = (Person) obj;18     if (age != other.age)19         return false;20     if (name == null) {21         if (other.name != null)22             return false;23     } else if (!name.equals(other.name))24         return false;25     return true;26 }

 

hashCode方法里为什么要使用31这个数字大概有两个原因:

  1. 31是一个质数,这样的数字在计算时可以尽量减少散列冲突。

  2. 可以提高执行效率,因为31*i=(i<<5)-i,31乘以一个数可以转换成移位操作,这样能快一点;但是也有网上一些人对这两点提出质疑。


TreeSet
  • 基于排序顺序实现不重复。

  • 实现了SortedSet接口,对集合元素自动排序。

  • 元素对象的类型必须实现Comparable接口,指定排序规则。

  • 通过CompareTo方法确定是否为重复元素。

 1 /** 2  * 使用TreeSet保存数据 3  * 存储结构:红黑树 4  * 要求:元素类必须实现Comparable接口,compareTo方法返回0,认为是重复元素 5  */ 6 public class Demo01 { 7     public static void main(String[] args) { 8  9         TreeSet<Person> treeSet = new TreeSet<>();10 11         Person p1 = new Person("it",12);12         Person p2 = new Person("he",12);13         Person p3 = new Person("she",12);14 15         //1.添加元素16         treeSet.add(p1);17         treeSet.add(p2);18         treeSet.add(p3);19         treeSet.add(p3);//相同无法添加20         //注:直接添加会报类型错误,需要实现Comparable接口21         System.out.println("元素个数为:"+treeSet.size());22         System.out.println(treeSet.toString());23 24         //2.删除元素25         treeSet.remove(p3);26         treeSet.remove(new Person("she",12));27         System.out.println(treeSet.toString());28 29         //3.遍历30         //3.1 增强for循环31         for (Person person:treeSet) {32             System.out.println(person);33         }34         //3.2 迭代器35         Iterator<Person> iterator = treeSet.iterator();36         while (iterator.hasNext()){37             System.out.println(iterator.next());38         }39         40         //4.判断41         System.out.println(treeSet.contains(new Person("it",12)));42     }43 }

 运行报错,因为treeset是红黑树,当前代码没有定义比较的属性,需要重写Comparable方法

先查看Comparable接口的源码,发现只有一个compareTo抽象方法,在Person类中实现它:

1 public class Person implements Comparable<Person> {2     @Override3     public int compareTo(Person o) {4         int n1 = this.getName().compareTo(o.getName());//名字相同,返回0,不同返回其他数字5         int n2 = this.getAge()-o.getAge();//比较年龄6         return n1==0?n2:n1;//名字相同,返回n2,名字不同,返回n17     }8 }

再次运行TreeSet的Demo01,运行成功

除了实现Comparable接口里的比较方法,还有另外一种方法,TreeSet也提供了一个带比较器Comparator的构造方法,使用匿名内部类来实现它:

 1 /** 2  * TreeSet的使用 3  * Comparator:实现定制比较(比较器) 4  */ 5 public class Demo02 { 6     public static void main(String[] args) { 7         TreeSet<Person> person = new TreeSet<Person>(new Comparator<Person>() { 8             @Override 9             public int compare(Person o1, Person o2) {10                 // 先按年龄比较11                 // 再按姓名比较12                 int n1=o1.getAge()-o2.getAge();13                 int n2=o1.getName().compareTo(o2.getName());14                 return n1==0?n2:n1;15             }16         });17         Person p1 = new Person("it",12);18         Person p2 = new Person("he",12);19         Person p3 = new Person("she",12);20 21         //1.添加元素22         person.add(p1);23         person.add(p2);24         person.add(p3);25         //注:直接添加会报类型错误,需要实现Comparable接口26         System.out.println("元素个数为:"+person.size());27         System.out.println(person.toString());28     }29 }

接下来我们来做一个小案例:

 1 /** 2  * 要求:使用TreeSet集合实现字符串按照长度进行排序 3  * helloworld tangrui hechengyang wangzixu yuguoming 4  * Comparator接口实现定制比较 5  */ 6 public class Demo03 { 7     public static void main(String[] args) { 8         TreeSet<String> s = new TreeSet<>(new Comparator<String>() { 9             @Override10             //1.先比较长度11             //2.在比较字符串12             public int compare(String o1, String o2) {13                 int n1 = o1.length()-o2.length();14                 int n2 = o1.compareTo(o2);15                 return n1==0?n2:n1;16             }17         });18         s.add("hello world");19         s.add("hello Java");20         s.add("hello Python");21         System.out.println(s.toString());22     }23 }

 

 


Map体系集合

  • Map接口的特点:

    1. 用于存储任意键值对(Key-Value)。

    2. 键:无序、无下标、不允许重复(唯一)。

    3. 值:无序、无下标、允许重复。


Map集合概述

  • 特点:存储一对数据(Key-Value),无序、无下标,键不可重复。

  • 方法:

    • V put(K key,V value)//将对象存入到集合中,关联键值。key重复则覆盖原值。

  • Object get(Object key)//根据键获取相应的值。

    • Set<K>//返回所有的key

    • Collection<V> values()//返回包含所有值的Collection集合。

    • Set<Map.Entry<K,V>>//键值匹配的set集合

 1 /** 2  * Map接口的使用 3  * 特点:1.存储键值对 2.键不能重复,值可以重复 3.无序 4  */ 5 public class Demo01 { 6     public static void main(String[] args) { 7         //创建Map集合 8         HashMap<String, String> map = new HashMap<>(); 9 10         //1.添加元素11         map.put("cn","中国");12         map.put("uk","英国");13         map.put("usa","美国");14         map.put("cn","Chinse");//相同key添加,会替换之前的value值15         System.out.println("元素个数为:"+map.size());16         System.out.println(map.toString());17 18         //2.删除19         map.remove("usa");20         System.out.println("删除后元素个数为:"+map.size());21         System.out.println(map.toString());22 23         //3.遍历24         //3.1 使用keyset(),先获取key,再使用get(key)方法获取value值25         Set<String> keySet = map.keySet();26         for (String key:keySet) {27             System.out.println(key+"-----"+map.get(key));28         }29         //简写30         for (String s:map.keySet()) {31             System.out.println(s+"-----"+map.get(s));32         }33         //3.2 使用entrySet()方法;效率更高,把key和value都拿出来了,不需要为了value再去多做一遍查询key的操作34         Set<Map.Entry<String, String>> entries = map.entrySet();35         for (Map.Entry<String,String> entry:entries) {36             System.out.println(entry.getKey()+"------"+entry.getValue());37         }38         //简写39         for (Map.Entry<String,String> entry:map.entrySet()) {40             System.out.println(entry.getKey()+"------"+entry.getValue());41         }42 43 44     }45 }

keySet遍历和entrySet的区别

 


Map集合的实现类

HashMap【重点】

  • JDK1.2版本,线程不安全,运行效率快;允许用null作为key或是value。

 1 /** 2  * 学生类 3  */ 4 public class Student { 5     private String name; 6     private int age; 7  8     public String getName() { 9         return name;10     }11 12     public void setName(String name) {13         this.name = name;14     }15 16     public int getAge() {17         return age;18     }19 20     public void setAge(int age) {21         this.age = age;22     }23 24     @Override25     public String toString() {26         return "Student{" +27                 "name='" + name + '\'' +28                 ", age=" + age +29                 '}';30     }31 32     public Student(String name, int age) {33         this.name = name;34         this.age = age;35     }36 }
 1 /** 2  * HashMap的使用 3  * 存储结构:哈希表(数组+链表+红黑树) 4  */ 5 public class Demo01 { 6     public static void main(String[] args) { 7         HashMap<Student, String> hashMap = new HashMap<>(); 8         Student p1 = new Student("赵一",11); 9         Student p2 = new Student("王二",11);10         Student p3 = new Student("张三",11);11         Student p4 = new Student("李四",11);12         //1.添加元素13         hashMap.put(p1,"北京");14         hashMap.put(p2,"上海");15         hashMap.put(p3,"广州");16         hashMap.put(p4,"曹县");17         hashMap.put(p4,"曹县(山东)");//不能添加重复的key,但是会将value更新18 19         //

更多文章请关注《万象专栏》