集合(详细解析)

目录

一、集合概述

二、Collection

1、Collection集合概述

2、创建Collection集合的对象

3、Collection集合常用方法

4、Collection集合的遍历

三、List集合

1、List集合概述和特点

2、List集合的特有方法

3、并发修改异常(ConcurrentModificationException)

4、增强for循环

四、Set集合

1、Set集合

2、Set集合特点

3、Hash值

4、HashSet集合特点

5、HashSet集合保证元素唯一性的源码分析

6、LinkedHashCodeSet集合概述和特点

7、TreeSet集合概述和特点

五、泛型

1、泛型的概述和好处

2、泛型类

3、泛型方法

4、泛型接口

5、类型通配符

6、可变参数

7、可变参数的使用

六、Map集合

1、Map集合的概述与特点

2、Map集合的基本功能

3、Map集合的获取功能

4、Map的遍历方法


一、集合概述

(1)集合的特点:提供一种存储空间可变的模型,存储的数据容量可以随时发生改变

(2)集合与数组的区别

1.长度上的区别:数组的长度是固定,设定后就无法变化的,而集合的存储长度是动态可变,相较于数组来说更加的灵活,能满足更多的需求。

2.存储内容上的区别:对于数组来说,数组的存储内容可以是基本类型,也可以是引用类型,然而集合的存储内容只能是引用类型

3.存储元素上的区别:对于数组来说,数组的存储元素有且仅有一种类型,然而集合可以存储不同类型的存储元素,即集合存储元素类型不止一种

(3)集合的体系结构

集合分为单列集合Collection及双列集合Map,其中单列集合Collection下又分可重复集合List与不可重复集合Set等。此处提及的Collection、Map、List、Set集合全为接口,其不可实例化需要对应的实现类来实现。

集合体系结构框架图:

二、Collection

1、Collection集合概述

(1)是单列集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素

(2)JDK不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现

2、创建Collection集合的对象

(1)以多态的方式来创建对象

(2)具体的实现类ArrayList

如接口Collection以多态的方式,通过实现类ArrayList来实现接口的中的方法add();

下面是代码示例:

/**
 * @author Tweek
 */
public class CollectionDemo1 {
    /**
     * 创建Collection集合对象的方法
     *   以多态的方式  ArrayList()
     */
    public static void main(String[] args) {
        //创建Collection集合的对象
        Collection<String> collection = new ArrayList<String>();
        //添加对象
        collection.add("Have");
        collection.add("A");
        collection.add("Nice");
        collection.add("Day ");
        //输出集合对象
        System.out.println(collection);
    }
}
​
控制台输出:
[Have, A, Nice, Day ]

3、Collection集合常用方法

方法名说明
boolean add(E e)添加元素
boolean remove(Object o)从集合中移除指定的元素
void clear()清空集合中的元素
boolean contains(Object o)判断集合中是否存在指定的元素
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中元素的个数

下面是代码示例,能让我们更好地了解Collection的常用方法:

/**
 * @author Tweek
 */
public class CollectionDemo2 {
    /**
     * 1.bool add(E e)
     * 2.bool remove(Object o)
     * 3.int size()
     * 4.bool contains(Object o)
     * 5.bool isEmpty()
     * 6.void clear()
     */
    public static void main(String[] args) {
        //创建集合对象
        Collection<String> cl = new ArrayList<String>();
        //1.添加元素add()
        cl.add("Tweek");
        cl.add("Meg");
        cl.add("Chris");
        cl.add("Peter");
        cl.add("Louis");
        System.out.println("Use the method 'add'");
        System.out.println(cl);
        System.out.println("---------------------------------");
        //2.移除指定元素
        cl.remove("Meg");
        System.out.println("Use the method 'remove'");
        System.out.println(cl);
        System.out.println("---------------------------------");
        //3.集合长度
        System.out.println(cl);
        System.out.println("The size of This Collection: "+cl.size());
        System.out.println("---------------------------------");
        //4.判断集合中是否存在指定元素
        System.out.println(cl);
        System.out.println("'Peter' is in This Collection! True or False?");
        System.out.println(cl.contains("Peter"));
        System.out.println("---------------------------------");
        //5.判断集合是否为空
        System.out.println(cl);
        System.out.println("This Collection is Empty! True or False? ");
        System.out.println(cl.isEmpty());
        System.out.println("---------------------------------");
        //6.清空集合元素,并判断是否为空
        System.out.print("Before 'clear' :");
        System.out.println(cl);
        cl.clear();
        System.out.print("After 'clear' :");
        System.out.println(cl);
        System.out.println("This Collection is Empty! True or False? ");
        System.out.println(cl.isEmpty());
    }
}
​
控制台输出:
Use the method 'add'
[Tweek, Meg, Chris, Peter, Louis]
---------------------------------
Use the method 'remove'
[Tweek, Chris, Peter, Louis]
---------------------------------
[Tweek, Chris, Peter, Louis]
The size of This Collection: 4
---------------------------------
[Tweek, Chris, Peter, Louis]
'Peter' is in This Collection! True or False?
true
---------------------------------
[Tweek, Chris, Peter, Louis]
This Collection is Empty! True or False? 
false
---------------------------------
Before 'clear' :[Tweek, Chris, Peter, Louis]
After 'clear' :[]
This Collection is Empty! True or False? 
true

4、Collection集合的遍历

(1)terator

Iterator:迭代器,集合的专用遍历方式。其本质是一个接口,故不能实例化,需要实现类来实现,即需以多态的方式。

1.Iterator<E>iterator(): 返回此集合中元素中的迭代器,通过集合的iterator()方法得到

2.迭代器是依赖于集合而存在的

3.格式

//创建集合
        Collection<String> cl = new ArrayList<String>();
        //遍历集合,迭代器方法
      //Iterator<E> 迭代器名 = 集合名.iterator()
        Iterator<String> it = cl.iterator();

(2)Iterator的常用方法

方法名作用
E next()返回迭代中的下一个元素
boolean hasNext()如果迭代具有更多元素,则返回true

1.E next()

利用此方法获取集合中的元素

下面是代码示例:

/**
 * @author Tweek
 */
public class CollectionDemo3 {
    public static void main(String[] args) {
        //创建集合对象
        Collection<String> cl = new ArrayList<String>();
        //添加元素
        cl.add("Tweek");
        cl.add("Meg");
        cl.add("Chris");
        cl.add("Peter");
        cl.add("Louis");
        // Iterator<E> iterator()。返回此集合中元素的迭代器,通过集合的iterator方法()得到
        Iterator<String> it = cl.iterator();
        // E next()
        System.out.println(it.next());
        System.out.println(it.next());
        System.out.println(it.next());
        System.out.println(it.next());
        System.out.println(it.next());
    }
}
​
控制台输出:
Tweek
Meg
Chris
Peter
Louis

2.boolean hasNext()

判断能否继续迭代,即判断是否存在下一个元素。

下面是代码示例:

/**
 * @author Tweek
 */
public class CollectionDemo3 {
    public static void main(String[] args) {
        //创建集合对象
        Collection<String> cl = new ArrayList<String>();
        //添加元素
        cl.add("Tweek");
        cl.add("Meg");
        cl.add("Chris");
        cl.add("Peter");
        cl.add("Louis");
        // Iterator<E> iterator()。返回此集合中元素的迭代器,通过集合的iterator方法()得到
        Iterator<String> it = cl.iterator();
        //boolean hasNext()
        while(it.hasNext()){
            System.out.println(it.next());
        }
        //迭代完集合内元素后再判断是否存在下一个元素
        //此处hasNext判断为false,故无输出
        if(it.hasNext()){
            System.out.println(it.next());
        }
    }
}
​
控制台输出:
Tweek
Meg
Chris
Peter
Louis 
       

(3)综合例子

例子:创建学生类,Collection存储学生类对象,并遍历

下面的代码示例,能让我们更好地理解迭代器:

/**
 * @author Tweek
 */
public class Student {
    /**
       学生类
    */
    private String name;
    private int age;
    private int score;
​
    public Student() {
    }
​
    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
​
    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;
    }
​
    public int getScore() {
        return score;
    }
​
    public void setScore(int score) {
        this.score = score;
    }
}
​
public class CollectionStudentsTest {
    /**
     * Collection存储学生类对象,并遍历
     * @param args
     */
    public static void main(String[] args) {
        //创建学生类对象
        Student stu1 = new Student("Tweek",16,100);
        Student stu2 = new Student("Peter",32,54);
        Student stu3 = new Student("Chris",17,65);
        Student stu4 = new Student("Meg",17,0);
        Student stu5 = new Student("Louis",32,87);
        //创建集合
        Collection<Student> stucl = new ArrayList<Student>();
        stucl.add(stu1);
        stucl.add(stu2);
        stucl.add(stu3);
        stucl.add(stu4);
        stucl.add(stu5);
        //遍历集合,迭代器方法
        Iterator<Student> it = stucl.iterator();
        while(it.hasNext()){
            Student s = it.next();
            System.out.println("Name: "+s.getName()+"  Age: " +
                    s.getAge()+
                    "  Score: "+s.getScore());
        }
    }
}

三、List集合

1、List集合概述和特点

(1)概述

List集合是有序集合(也称序列),使用List集合,用户可以控制列表中每一个元素的插入位置,用户也可以通过整数索引来访问元素,也可以用整数索引来搜索列表中的元素

与Set集合不同的是,List集合允许重复的元素,而Set集合不允许有重复的元素

(2)特点

1.有序:存储和取出的元素顺序一致,类似于队列,FIFO先进先出

2、可重复:存储的元素可以重复

下面的代码示例,能让我们更好地理解List的特点

/**
 * @author Tweek
 */
public class ListDemo1 {
    /**
        List的特点
        1、有序
        2、重复
     */
    public static void main(String[] args) {
        //创建List集合
        List<String> ls = new ArrayList<>();
        //添加元素,集合中出现重复元素Tweek
        ls.add("Tweek");
        ls.add("Meg");
        ls.add("Chris");
        ls.add("Peter");
        ls.add("Louis");
        ls.add("Tweek");
        //用迭代器遍历
        Iterator<String> it = ls.iterator();
        //若有序,则list中的元素顺序与添加元素顺序一致
         while(it.hasNext()){
             System.out.println(it.next());
         }
    }
}
​
控制台输出:
Tweek
Meg
Chris
Peter
Louis
Tweek

2、List集合的特有方法

方法名说明
void add(int index,E element)在此集合中的指定位置插入指定元素
E remove(int index)删除指定索引位置的元素,返回被删除的元素
E set(int index,E element)修改指定索引位置的元素,返回被修改的元素
E get(int index)返回指定索引位置的元素

下面的代码示例,能让我们更好地理解List集合的特有方法:

/**
 * @author Tweek
 */
public class ListDemo2 {
    /**
     * List集合的特有方法
     * 1.void add(int index,E element)
     * 2.E remove(int index)
     * 3.E set(int index,E element)
     * 4.E get(int index)
     */
    public static void main(String[] args) {
        //创建集合
        List<String> ls  = new ArrayList<>();
        //1.添加元素add()
        addElement(ls);
        //2.移除索引位置元素并返回移除元素
        removeElement(ls);
        //3.修改索引位置元素并返回被修改元素
        changeElement(ls);
        //4.返回索引位置元素
        getElement(ls);
    }
​
    private static void addElement(List<String> ls) {
        ls.add("Tweek");
        ls.add("Meg");
        ls.add("Chris");
        ls.add("Peter");
        ls.add("Louis");
        ls.add("Tweek");
        System.out.print("我添加了这些元素: ");
        System.out.println(ls);
        System.out.println("---------------------------");
    }
​
    private static void getElement(List<String> ls) {
        int index = 0;
        System.out.print("我获取到第"+(index+1)+"个元素:");
        System.out.println(ls.get(index));
    }
​
    private static void changeElement(List<String> ls) {
        int index=1;
        System.out.print("修改第"+index+"元素前: ");
        System.out.println(ls);
        System.out.print("被修改的元素为:");
        System.out.println(ls.set(index,"Brian"));
        System.out.print("修改第"+index+"元素后: ");
        System.out.println(ls);
        System.out.println("---------------------------");
    }
​
    private static void removeElement(List<String> ls) {
        int index=5;
        System.out.print("移除第"+index+"元素前: ");
        System.out.println(ls);
        System.out.print("所移除的元素为:");
        System.out.println(ls.remove(index));
        System.out.print("移除第"+index+"元素后: ");
        System.out.println(ls);
        System.out.println("---------------------------");
    }
}
​
控制台输出:
我添加了这些元素: [Tweek, Meg, Chris, Peter, Louis, Tweek]
---------------------------
移除第5元素前: [Tweek, Meg, Chris, Peter, Louis, Tweek]
所移除的元素为:Tweek
移除第5元素后: [Tweek, Meg, Chris, Peter, Louis]
---------------------------
修改第1元素前: [Tweek, Meg, Chris, Peter, Louis]
被修改的元素为:Meg
修改第1元素后: [Tweek, Brian, Chris, Peter, Louis]
---------------------------
我获取到第1个元素:Tweek

3、综合例子

创建一个存储学生对象的集合,存储3个学生对象,并在遍历学生对象信息打印在控制台

下面的例子代码,能让我们更好地理解List集合

/**
 * @author Tweek
 */
public class Student {
    private String name;
    private int age;
    private int score;
​
    public Student() {
    }
​
    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
​
    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;
    }
​
    public int getScore() {
        return score;
    }
​
    public void setScore(int score) {
        this.score = score;
    }
}
​
public class ListStudentsTest {
    public static void main(String[] args) {
        //创建学生类对象
        Student stu1 = new Student("Tweek",16,100);
        Student stu2 = new Student("Peter",32,54);
        Student stu3 = new Student("Chris",17,65);
        Student stu4 = new Student("Meg",17,0);
        Student stu5 = new Student("Louis",32,87);
        //创建List集合
        List<Student> ls = new ArrayList<>();
        ls.add(stu1);
        ls.add(stu2);
        ls.add(stu3);
        ls.add(stu4);
        ls.add(stu5);
        //遍历集合,用迭代器的方法
        System.out.println("遍历List集合,用迭代器");
        Iterator<Student> it = ls.iterator();
        while(it.hasNext()){
            Student stu = it.next();
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
        System.out.println("------------------------------------------");
        //for循环方法
        System.out.println("遍历List集合,用for循环");
        for (int i = 0; i < ls.size(); i++) {
            Student stu = ls.get(i);
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
​
    }
}
​
控制台输出:
遍历List集合,用迭代器
Name: Tweek  Age: 16  Score: 100
Name: Peter  Age: 32  Score: 54
Name: Chris  Age: 17  Score: 65
Name: Meg  Age: 17  Score: 0
Name: Louis  Age: 32  Score: 87
------------------------------------------
遍历List集合,用for循环
Name: Tweek  Age: 16  Score: 100
Name: Peter  Age: 32  Score: 54
Name: Chris  Age: 17  Score: 65
Name: Meg  Age: 17  Score: 0
Name: Louis  Age: 32  Score: 87

3、并发修改异常(ConcurrentModificationException)

(1)并发修改异常

当不允许这样修改时,可以通过检测到对象的并发修改方法来抛出此异常。像是说,一个线程通常不允许修改集合,而另一个线程正在遍历这个集合,就会出现并发修改异常。

下面的代码示例给出的例子,能让我们更好的了解并发修改异常的发生情景:

/**
 * @author Tweek
 */
public class ListDemo1 {
    /**
        List集合正在遍历的同时进行元素添加操作,会出现并发修改异常
     */
    public static void main(String[] args) {
        //创建List集合
        List<String> ls = new ArrayList<>();
        //添加元素
        ls.add("Tweek");
        ls.add("Meg");
        ls.add("Chris");
        ls.add("Peter");
        ls.add("Louis");
        //用迭代器遍历
        Iterator<String> it = ls.iterator();
        //遍历集合的同时添加元素
         while(it.hasNext()){
             String s = it.next();
             if(s.equals("Meg")){
                 ls.add("Meg");
             }
         }
    }
}
​
/**
    此时控制台提示出现ConcurrentModificationException(并发修改异常)与提示next()方法中的checkForComodification方法出了问题
*/
控制台输出:
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 Collection.itemout.mList.ListDemo1.main(ListDemo1.java:29)

下面的代码示例是解决集合遍历中添加与if判断中调用equals函数的字符串常量不同的元素不出现并发修改异常的方法:

/**
 * @author Tweek
 */
public class ListDemo3 {
    /**
        遍历集合的过程中,添加元素
     */
    public static void main(String[] args) {
        //创建List集合
        List<String> ls = new ArrayList<>();
        //添加元素
        ls.add("Tweek");
        ls.add("Meg");
        ls.add("Chris");
        ls.add("Peter");
        ls.add("Louis");
​
        //遍历集合的同时,添加元素
        for (int i = 0; i < ls.size(); i++) {
            String s = ls.get(i);
            if("Meg".equals(s)){
                ls.add("Tweek");
                break;
            }
        }
        System.out.println(ls);
    }
}
​
控制台输出:
[Tweek, Meg, Chris, Peter, Louis, Tweek]

(2)OutOfMemoryError错误出现

注意:在对字符串常量调用equals函数作判断且找到当前元素后未执行break语句时,不能添加与当前字符串常量重复元素,否则会使得在集合末尾不断自动扩容添加一个相同的元素,使得此时for循环进入死循环并出现OutOfMemoryError: Java heap space错误提示,这是因为JVM创建的对象太多了,在进行垃圾回收的过程中,JVM分配到堆内存空间已经用满了,与heap space有关。

下面的代码示例是出现OutOfMemoryError的代码示例:

/**
 * @author Tweek
 */
public class ListDemo3 {
    /**
     出现OutOfMemoryError错误提示
     */
    public static void main(String[] args) {
        //创建List集合
        List<String> ls = new ArrayList<>();
        //添加元素
        ls.add("Tweek");
        ls.add("Meg");
        ls.add("Chris");
        ls.add("Peter");
        ls.add("Louis");
​
        //遍历集合的同时,添加元素
        for (int i = 0; i < ls.size(); i++) {
            String s = ls.get(i);
            /**
             * 此处进行equals元素比较的同时,添加了重复元素“Meg”导致OutOfMemoryError的出现
             * 找到Meg元素后不执行break就会使得list末尾处不断自动扩容添加Meg,使得for循环进入死循环就会出现OutOfMemoryError
             * 若执行break语句,则不会出现OutOfMemoryError错误提示
             */
            if("Meg".equals(s)){
                ls.add("Meg");
            }
        }
        System.out.println(ls);
    }
}
​
控制台输出:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3210)
    at java.util.Arrays.copyOf(Arrays.java:3181)
    at java.util.ArrayList.grow(ArrayList.java:261)
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
    at java.util.ArrayList.add(ArrayList.java:458)
    at Collection.itemout.mList.ListDemo3.main(ListDemo3.java:32)

(3)并发修改异常产生的原因

在我们使用迭代器遍历集合的过程中,我们进行了集合add()方法添加元素的操作,导致next()方法内的变量modCount(实际修改次数)额外加一,使其与另一变量预期修改集合的次数expectedModCount不相等,致使next()方法内的checkForComodification方法内的modCount与expectedModCount判断为不相等,使其抛出并发修改异常ConcurrentModificationException

下面的代码示例是ConcurrentModificationException出现的源码分析:

public interface List<E>{
    Iterator<E> iterator();
    boolean add(E e);
}
public class AbstractList<E>{
    protected  int modCount = 0;
}
public class ArrayList<E> extends AbstractList<E> implements List<E>{
        public Iterator<E> iterator() {
                return new Itr();
            }
        /**
            在get方法内没有做出checkForComodification的判断,所以不会抛出异常
        */
        public E get(int index) {
           rangeCheck(index);
           return elementData(index);
        }
​
        public boolean add(E e) {
            /**
               执行add操作,使得modCount额外加一
            */
               modCount++;
               add(e,elementData,size);
               return true;
         }
​
        private class Itr implements Iterator<E> {
            int expectedModCount = modCount;
               
            /**
                 modCount:实际修改集合的次数
                 expectedModCount:预期修改集合的次数
                 初始值都从0开始。
            */
            public E next() {
                checkForComodification();
                int i = cursor;
                if (i >= size)
                    throw new NoSuchElementException();
                    Object[] elementData = ArrayList.this.elementData;
                    if (i >= elementData.length)
                        throw new ConcurrentModificationException();
                    cursor = i + 1;
                    return (E) elementData[lastRet = i];
                }
​
                final void checkForComodification() {
                    /**
                      modCount与expectedModCount不相等时
                      抛出异常ConcurrentModificationException
                     */
                    if (modCount != expectedModCount)
                        throw new ConcurrentModificationException();
                    }
         }
}

(4)列表迭代器(ListIterator)

ListIterator:列表迭代器

1.通过List集合的listiterator()方法得到,所以说它是List集合特有的迭代器

2、用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置

3、列表迭代器的所有方法

方法说明
void add(E e)将指定的元素插入列表(可选操作)
boolean hasNext()如果此列表迭代器在向前方向遍历列表时具有更多的元素,则返回true
E next()返回列表中的下一个元素,并且前进光标位置
boolean hasPrevious()如果此列表迭代器在向后方向遍历列表时具有更多的元素,则返回true
int nextIndex()返回由后续调用返回的元素的索引next()
E previous()返回列表中的下一个元素,并且前进光标位置
int previousIndex()返回由后续调用返回的元素的索引previous()
void remove()从列表中删除next()或previous()(可选操作)返回的最后一个元素
void set(E e)用指定的元素(可选操作)替换next()或previous()返回的最后一个元素

列表迭代器常用的方法:

方法说明
E next()返回迭代中的下一个元素
boolean hasNext()如果此列表迭代器在向前方向遍历列表时具有更多的元素,则返回true
E previous()返回列表中的上一个元素
boolean hasPrevious()如果此列表迭代器在向后方向遍历列表时具有更多的元素,则返回true
void add(E e)将指定元素插入列表

下面的代码示例,能让我们更好地理解列表迭代器的常用方法:

/**
 * @author Tweek
 */
public class ListIteratorDemo {
    public static void main(String[] args) {
        /**
         * ListIterator中常用的方法
         * 1.E next()
         * 2.boolean hasNext()
         * 3.E previous()
         * 4.boolean hasPrevious()
         * 5.void add(E e)
         */
        //创建List集合
        List<String> ls = new ArrayList<>();
        //添加元素
        ls.add("Tweek");
        ls.add("Meg");
        ls.add("Chris");
        ls.add("Peter");
        ls.add("Louis");
        //通过List集合的listiterator()方法获得
        ListIterator<String> lit = ls.listIterator();
        //1.E next() 2.boolean hasNext()
        while(lit.hasNext()){
            String s = lit.next();
            System.out.println(s);
        }
        System.out.println("------------------");
        //3.E previous() 4.boolean hasPrevious()
        while(lit.hasPrevious()){
            String s2 = lit.previous();
            System.out.println(s2);
        }
        System.out.println("------------------");
        //5.void add()
        while(lit.hasNext()){
            String s3 = lit.next();
            if("Meg".equals(s3)){
                lit.add("Biden");
                //此处不用执行break语句,因为不同与迭代器Iterator的add方法使List自动扩容并把元素插入队尾,列表迭代器是插入指定E在当前if判断的字符串常量后面。
            }
        }
        System.out.println(ls);
    }
}

控制台输出:
Tweek
Meg
Chris
Peter
Louis
------------------
Louis
Peter
Chris
Meg
Tweek
------------------
[Tweek, Meg, Biden, Chris, Peter, Louis]

问题:为什么此处在遍历的同时,执行add操作不会发生并发修改异常ConcurrentModificationException呢?

回答:因为列表迭代器ListIterator的add()方法内,expectedModCount能更新出现修改后的modCount,故能始终保持相等,从而不会抛出并发修改异常ConcurrentModificationException

下面的代码示例,是以上问题的源码分析:

public interface List<E>{
    Iterator<E> iterator();
    ListIterator<E> listIterator();
}
public class AbstractList<E>{
    protected  int modCount = 0;
}
public class ArrayList<E> extends AbstractList<E> implements List<E>{
        public Iterator<E> iterator() {
                return new Itr();
            }
​
        private class Itr implements Iterator<E> {
            ...
         }
​
        public ListIterator<E> listIterator() {
            return new ListItr(0);
        }
​
        private class ListItr extends Itr implements ListIterator<E> {
            public void add(E e) {
                checkForComodification();
​
                try {
                    int i = cursor;
                    ArrayList.this.add(i, e);
                    cursor = i + 1;
                    lastRet = -1;
                   //保证了这两个值一直相等,当modCount发生改变时,此处会使得expectedModCount更新与modCount相同的值,故不会造成并发修改异常 
                    expectedModCount = modCount;  
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }
        }
}

4、增强for循环

增强for:简化数组和Collection集合的遍历

(1)实现Iterable接口的类允许其对象成为增强型for语句的目标

(2)它是JDK5之后出现的,其内部原理是一个Iterator迭代器

(3)格式

 for(元素数据类型变量名 : 数组或者Collection集合){
   //在此处使用变量即可,该变量就是元素
} 
示例:
    int[] arr = {1,2,3,4};
 fpr(int i : arr){
     System.out.println(i)
 }
​
控制台输出:
1
2
3
4

下面的代码示例,能让我们更好地理解增强for循环的应用:

/**
 * @author Tweek
 */
public class Form {
    public static void main(String[] args) {
        /**
         * 增强for应用示例
         * 1.数组
         * 2.列表List
         */
        String[] str = {"Tweek","Jim","Peter"};
        for(String s:str){
            System.out.println(s);
        }
        System.out.println("-------------------------");
        List<String> ls = new ArrayList<>();
        ls.add("Tweek");
        ls.add("Peter");
        ls.add("Jim");
        for(String s:ls){
            System.out.println(s);
        }
        System.out.println("-------------------------");
        //内部原理是一个Iterator迭代器
        //因为作为Iterator迭代器其在一个线程不允许修改集合的时候,另一个线程正在遍历。会抛出ConcurrentModificationException并发修改异常
        for(String s:ls){
            if("Peter".equals(s)){
                ls.add("Meg");
            }
        }
    }
}
​
控制台输出:
Tweek
Jim
Peter
-------------------------
Tweek
Peter
Jim
-------------------------
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 Collection.itemout.mList.Form.main(Form.java:27)
​
Process finished with exit code 1

5、List的遍历综合例子

创建一个存储学生对象的集合,存储5个学生对象,用程序实现在控制台的遍历。

下面是代码示例:

/**
 * @author Tweek
 */
public class ListStudentsTest {
    public static void main(String[] args) {
        //创建学生类对象
        Student stu1 = new Student("Tweek",16,100);
        Student stu2 = new Student("Peter",32,54);
        Student stu3 = new Student("Chris",17,65);
        Student stu4 = new Student("Meg",17,0);
        Student stu5 = new Student("Louis",32,87);
        //创建List集合
        List<Student> ls = new ArrayList<>();
        ls.add(stu1);
        ls.add(stu2);
        ls.add(stu3);
        ls.add(stu4);
        ls.add(stu5);
        //遍历集合,用迭代器的方法
        System.out.println("遍历List集合,用迭代器");
        Iterator<Student> it = ls.iterator();
        while(it.hasNext()){
            Student stu = it.next();
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
        System.out.println("------------------------------------------");
        //for循环方法
        System.out.println("遍历List集合,用for循环");
        for (int i = 0; i < ls.size(); i++) {
            Student stu = ls.get(i);
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
        System.out.println("------------------------------------------");
        //增强for循环
        System.out.println("遍历List集合,用增强for循环");
        for(Student stu : ls){
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
    }
}
​
控制台输出:
遍历List集合,用迭代器
Name: Tweek  Age: 16  Score: 100
Name: Peter  Age: 32  Score: 54
Name: Chris  Age: 17  Score: 65
Name: Meg  Age: 17  Score: 0
Name: Louis  Age: 32  Score: 87
------------------------------------------
遍历List集合,用for循环
Name: Tweek  Age: 16  Score: 100
Name: Peter  Age: 32  Score: 54
Name: Chris  Age: 17  Score: 65
Name: Meg  Age: 17  Score: 0
Name: Louis  Age: 32  Score: 87
------------------------------------------
遍历List集合,用增强for循环
Name: Tweek  Age: 16  Score: 100
Name: Peter  Age: 32  Score: 54
Name: Chris  Age: 17  Score: 65
Name: Meg  Age: 17  Score: 0
Name: Louis  Age: 32  Score: 87

5、List集合子类的特点

List集合常用子类:ArrayList、LinkedList

特点:

(1)ArrayList

底层数据结构是数组,查询快,增删慢。因数组是按索引顺序存储的。

(2)LinkedList

底层数据结构是链表,查询慢,增删快。因链表是以指针来连接结点,增删只用增加或删除结点即可,而查询链表元素的话,需要遍历链表。

下面是代码示例,能让我们更好地了解List集合的子类

/**
 * @author Tweek
 */
public class SonOfList {
    public static void main(String[] args) {
        //创建集合对象
        ArrayList<String> arr = new ArrayList<>();
        arr.add("Tweek");
        arr.add("Jim");
        arr.add("Peter");
        //遍历ArrayList
        System.out.println("遍历ArrayList集合");
        for(String s:arr){
            System.out.println(s);
        }
        System.out.println("---------------------");
        //创建LinkedList
        LinkedList<String> lls = new LinkedList<>();
​
        lls.add("Tweek");
        lls.add("Jim");
        lls.add("Peter");
​
        System.out.println("遍历LinkedList集合");
        for(String s: lls){
            System.out.println(s);
        }
    }
}
​
控制台输出:
遍历ArrayList集合
Tweek
Jim
Peter
---------------------
遍历LinkedList集合
Tweek
Jim
Peter

(3)ArrayList遍历综合例子

创建一个存储学生对象的集合,存储5个学生对象,用程序实现在控制台的遍历。

下面是代码示例:

/**
 * @author Tweek
 */
public class ArrayListStudentsTest {
    public static void main(String[] args) {
        /**
         * 用三种方式遍历ArrayList
         */
        //创建学生类对象
        Student stu1 = new Student("Tweek",16,100);
        Student stu2 = new Student("Peter",32,54);
        Student stu3 = new Student("Chris",17,65);
        Student stu4 = new Student("Meg",17,0);
        Student stu5 = new Student("Louis",32,87);
        //创建ArrayList集合
        ArrayList<Student> arr = new ArrayList<>();
        arr.add(stu1);
        arr.add(stu2);
        arr.add(stu3);
        arr.add(stu4);
        arr.add(stu5);
        //1.迭代器遍历ArrayList
        System.out.println("用迭代器遍历ArrayList");
        Iterator<Student> it = arr.iterator();
        while(it.hasNext()){
            Student stu = it.next();
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
        System.out.println("----------------------------");
        //2.用普通for循环遍历ArrayList
        System.out.println("用普通for循环遍历ArrayList");
        for (int i = 0; i < arr.size(); i++) {
            Student stu = arr.get(i);
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
        System.out.println("----------------------------");
        //3.用增强for循环遍历ArrayList
        System.out.println("用增强for循环遍历ArrayList");
        for(Student stu: arr){
​
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
    }
}
​
控制台输出:
用迭代器遍历ArrayList
Name: Tweek  Age: 16  Score: 100
Name: Peter  Age: 32  Score: 54
Name: Chris  Age: 17  Score: 65
Name: Meg  Age: 17  Score: 0
Name: Louis  Age: 32  Score: 87
----------------------------
用普通for循环遍历ArrayList
Name: Tweek  Age: 16  Score: 100
Name: Peter  Age: 32  Score: 54
Name: Chris  Age: 17  Score: 65
Name: Meg  Age: 17  Score: 0
Name: Louis  Age: 32  Score: 87
----------------------------
用增强for循环遍历ArrayList
Name: Tweek  Age: 16  Score: 100
Name: Peter  Age: 32  Score: 54
Name: Chris  Age: 17  Score: 65
Name: Meg  Age: 17  Score: 0
Name: Louis  Age: 32  Score: 87

(4)LinkedList集合的特有功能

LinkedList本质同ArrayList一样是一个迭代器

方法名说明
public void addFirst(E e)在该列表开头插入指定的元素
public void addLast(E e)将指定的元素追加到此列表的末尾
public E getFirst()返回此列表中的第一个元素
public E getLast()返回此列表中的最后一个元素
public E removeFist()从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素

下面的代码示例,能让我们更好地理解LinkedList集合的特有功能

/**
 * @author Tweek
 */
public class LinkedListCharacteristics {
    /**
     * LinkedList的常用方法
     * | public void addFirst(E e) | 在该列表开头插入指定的元素       |
     * | public void addLast(E e)  | 将指定的元素追加到此列表的末尾   |
     * | public E getFirst()       | 返回此列表中的第一个元素         |
     * | public E getLast()        | 返回此列表中的最后一个元素       |
     * | public E removeFist()     | 从此列表中删除并返回第一个元素   |
     * | public E removeLast()     | 从此列表中删除并返回最后一个元素 |
     * @param args
     */
    public static void main(String[] args) {
        //创建LinkedList集合
        LinkedList<String> lls = new LinkedList<>();
        lls.add("Tweek");
        lls.add("Jim");
        lls.add("Peter");
        lls.add("Jason");
        lls.add("Louis");
        //1.public void addFirst(E e)
        System.out.print("添加元素前:");
        System.out.println(lls);
        System.out.println("addFirst():");
        System.out.print("添加元素后: ");
        lls.addFirst("Hello");
        System.out.println(lls);
        System.out.println("-------------------------");
        //2.public void addLast(E e)
        System.out.print("添加元素前:");
        System.out.println(lls);
        System.out.println("addLast():");
        System.out.print("添加元素后: ");
        lls.addLast("World");
        System.out.println(lls);
        System.out.println("-------------------------");
        //3.public E getFirst();
        System.out.println("getFirst: ");
        System.out.print("获取第一个元素:");
        System.out.println(lls.getFirst());
        System.out.println("-------------------------");
        //4.public E getLast();
        System.out.println("getLast: ");
        System.out.print("获取最后一个个元素:");
        System.out.println(lls.getLast());
        System.out.println("-------------------------");
        //5.public E removeFirst()
        System.out.print("删除第一个元素前: ");
        System.out.println(lls);
        System.out.print("删除第一个元素 "+lls.removeFirst()+" 后");
        System.out.println(lls);
        System.out.println("-------------------------");
        //6.public E removeLast()
        System.out.print("删除最后一个元素前: ");
        System.out.println(lls);
        System.out.print("删除最后一个元素 "+lls.removeLast()+" 后");
        System.out.println(lls);
    }
}
​
控制台输出:
添加元素前:[Tweek, Jim, Peter, Jason, Louis]
addFirst:
添加元素后: [Hello, Tweek, Jim, Peter, Jason, Louis]
-------------------------
添加元素前:[Hello, Tweek, Jim, Peter, Jason, Louis]
addLast:
添加元素后: [Hello, Tweek, Jim, Peter, Jason, Louis, World]
-------------------------
getFirst: 
获取第一个元素:Hello
-------------------------
getLast: 
获取最后一个个元素:World
-------------------------
删除第一个元素前: [Hello, Tweek, Jim, Peter, Jason, Louis, World]
删除第一个元素 Hello 后[Tweek, Jim, Peter, Jason, Louis, World]
-------------------------
删除最后一个元素前: [Tweek, Jim, Peter, Jason, Louis, World]
删除最后一个元素 World 后[Tweek, Jim, Peter, Jason, Louis]

(5)LinkedList遍历的综合例子

创建一个存储学生对象的集合,存储5个学生对象,用程序实现在控制台的遍历。

下面是代码示例:

/**
 * @author Tweek
 */
public class LinkedListStudentsTest {
    public static void main(String[] args) {
        /**
         * 用三种遍历方法遍历LinkedList
         */
        //创建学生对象
        Student stu1 = new Student("Tweek",16,100);
        Student stu2 = new Student("Peter",32,54);
        Student stu3 = new Student("Chris",17,65);
        Student stu4 = new Student("Meg",17,0);
        Student stu5 = new Student("Louis",32,87);
        //创建LinkedList集合
        LinkedList<Student> lls = new LinkedList<>();
        lls.add(stu1);
        lls.add(stu2);
        lls.add(stu3);
        lls.add(stu4);
        lls.add(stu5);
        //1.迭代器遍历LinkedList
        System.out.println("用迭代器遍历LinkedList");
        Iterator<Student> it = lls.iterator();
        while(it.hasNext()){
            Student stu = it.next();
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
        System.out.println("----------------------------");
        //2.用普通for循环遍历LinkedList
        System.out.println("用普通for循环遍历LinkedList");
        for (int i = 0; i < lls.size(); i++) {
            Student stu = lls.get(i);
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
        System.out.println("----------------------------");
        //3.用增强for循环遍历LinkedList
        System.out.println("用增强for循环遍历LinkedList");
        for(Student stu: lls){
​
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
    }
}
​
控制台输出:
用迭代器遍历LinkedList
Name: Tweek  Age: 16  Score: 100
Name: Peter  Age: 32  Score: 54
Name: Chris  Age: 17  Score: 65
Name: Meg  Age: 17  Score: 0
Name: Louis  Age: 32  Score: 87
----------------------------
用普通for循环遍历LinkedList
Name: Tweek  Age: 16  Score: 100
Name: Peter  Age: 32  Score: 54
Name: Chris  Age: 17  Score: 65
Name: Meg  Age: 17  Score: 0
Name: Louis  Age: 32  Score: 87
----------------------------
用增强for循环遍历LinkedList
Name: Tweek  Age: 16  Score: 100
Name: Peter  Age: 32  Score: 54
Name: Chris  Age: 17  Score: 65
Name: Meg  Age: 17  Score: 0
Name: Louis  Age: 32  Score: 87

四、Set集合

1、Set集合

Set是一个接口继承于Collection接口,它的功能全继承于Collection。Set的实现类:HashSet(对集合的迭代顺序不作任何保证,换句话说就是迭代输出顺序不按输入顺序)

2、Set集合特点

(1)不包含重复元素的集合

(2)没有带索引的方法,所以不能用普通for循环遍历,可以用增强for循环

下面的代码示例,能让我们更好地理解Set集合

/**
 * @author Tweek
 */
public class SetDemo1 {
    /**
     * Set集合的特点
     * 1.不包含重复元素的集合
     * 2.没有带索引的方法,所以不能使用普通for循环遍历
     *
     * HashSet:对集合的迭代顺序不作任何的保证
     */
    public static void main(String[] args) {
        //创建集合对象
        Set<String> st = new HashSet<>();
        //添加元素
        st.add("Tweek");
        st.add("Jim");
        st.add("Peter");
        st.add("Louis");
        //不包含重复的元素
        st.add("Tweek");
        //用增强for遍历
        for(String s : st){
            System.out.println(s);
        }
    }
}
​
控制台输出:
Peter
Tweek
Jim
Louis

3、Hash值

(1)哈希值

其是指JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

(2)哈希值获取方法

Object类中有一个方法可以获取对象的哈希值

public int hashCode():返回对象的哈希码值

(3)对象的哈希值的特点

1.同一个对象多次调用hashCode()方法返回的哈希值是相同的

2.默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同。其原理只有一个常量池,在常量池中已经创建好的常量地址不变,后边新创建的常量也只是引用了已经创建好的常量地址。

下面的代码示例,能让我们更好地了解hashCode的运作:




/**
 * @author Tweek
 */
public class HashCodeDemo {
    /**
     * public int hashCode()
     */
    public static void main(String[] args) {
        Student stu1 = new Student("Tweek",15,87);
        Student stu2 = new Student("Jim",16,58);
        System.out.println("多次调用相同对象的hashCode:");
        System.out.println("stu1 HashCode: "+stu1.hashCode());
        System.out.println("stu1 HashCode: "+stu1.hashCode());
        System.out.println("---------------------------------------------");
        System.out.println("调用不同对象的hashCode:");
        System.out.println("stu1 HashCode: "+stu1.hashCode());
        System.out.println("stu2 HashCode: "+stu2.hashCode());
    }
}
​
控制台输出:
多次调用相同对象的hashCode:
stu1 HashCode: 1163157884
stu1 HashCode: 1163157884
---------------------------------------------
调用不同对象的hashCode:
stu1 HashCode: 1163157884
stu2 HashCode: 1956725890

4、HashSet集合特点

(1)底层数据结构是哈希表

(2)对集合的迭代顺序不作任何的表征,也就是说不保证存储和取出的元素顺序一致

(3)没有带索引的方法,所以不能用普通for循环遍历

(4)由于是Set集合,所以是不包含重复元素的集合

下面的代码示例,能让我们更好地了解HashSet集合特点:




/**
 * @author Tweek
 */
public class HashSetDemo1 {
    /**
     * (1)底层数据结构是哈希表
     *
     * (2)对集合的迭代顺序不作任何的表征,也就是说不保证存储和取出的元素顺序一致
     *
     * (3)没有带索引的方法,所以不能用普通for循环遍历
     *
     * (4)由于是Set集合,所以是不包含重复元素的集合
     * @param args
     */
    public static void main(String[] args) {
        //创建HashSet集合对象
        HashSet<String> hs = new HashSet<>();
        //添加元素
        hs.add("Tweek");
        hs.add("Jim");
        hs.add("Louis");
        //不能包含重复元素
        hs.add("Tweek");
        //用增强for循环遍历
        for(String s: hs){
            System.out.println(s);
        }
    }
}
​
控制台输出:
Tweek
Jim
Louis

5、HashSet集合保证元素唯一性的源码分析

(1)原理

HashSet集合通过元素的哈希值是否相同的判断保证了元素的唯一性。首先调用对象的hasCode方法获取对象的哈希值,然后根据对象的哈希值计算对象的存储位置,此时判断该位置是否有元素存在,如果没有则将元素存储到该位置,反之如果有则遍历该位置的所有元素,和新存入的元素比较哈希值是否相同,此时若是都不相同,则可将元素存储到该位置,若是有相同的,则调用equals方法比较对象内容是否相等,如果不相等就返回false并将元素存储到该位置,如果相等则返回true并说明该元素已重复,集合不存储该元素。

下面的源码示例,能让我们更好地了解HashSet集合保证元素唯一性的原因




public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
private transient HashMap<E,Object> map;
​
private static final Object PRESENT = new Object();
​
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
​
//hash值和元素的hashCode()方法相关
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
​
//如果哈希表未初始化,就对其进行初始化
if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;
    //根据对象的哈希值计算对象的储存位置,如果该位置没有元素,就储存元素
if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);
else {
    Node<K,V> e; K k;
    /*
    如果该位置有元素
        存入的元素和以前在这个位置的元素比较哈希值
            如果哈希值不同,会继续往下执行,把元素添加到集合中
            如果哈希值相同,会调用对象的equals()方法比较
                如果返回false,会继续往下执行,把元素添加到集合
                如果返回true,说明元素重复,不添加
    */
    if (p.hash == hash &&
        ((k = p.key) == key || (key != null && key.equals(k))))
        e = p;//重复不添加
    else if (p instanceof TreeNode)
        e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    else {
        for (int binCount = 0; ; ++binCount) {
            if ((e = p.next) == null) {
                p.next = newNode(hash, key, value, null);//添加了元素
                if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                    treeifyBin(tab, hash);
                break;
            }
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                break;
            p = e;
        }
    }
    if (e != null) { // existing mapping for key
        V oldValue = e.value;
        if (!onlyIfAbsent || oldValue == null)
            e.value = value;
        afterNodeAccess(e);
        return oldValue;
    }
}
++modCount;
if (++size > threshold)
    resize();
afterNodeInsertion(evict);
return null;
}

(2)HashSet集合存储元素

要保证元素唯一性,需要重写hashCode()和equals()方法

重写equals方法就得重写hashCode方法,equals比较的是两个对象的地址,不同的对象是不同的地址,但是如果我们要集合中不出现相同内容的不同对象,那么就得重写equals方法使其比较对象中的内容,同理重写hashCode方法。

下面的两段代码示例,能让我们更好地理解为什么要重写hashCode()与equals()方法

代码1:




/**
 * @author Tweek
 */
public class Student {
    private String name;
    private int age;
    private int score;
​
    public Student() {
    }
​
    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
​
    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;
    }
​
    public int getScore() {
        return score;
    }
​
    public void setScore(int score) {
        this.score = score;
    }
}
​
public class HashCodeDemo2 {
    /**
     * 未重写hashCode() and equals()
     */
    public static void main(String[] args) {
        //创建集合
        HashSet<Student> hs = new HashSet<>();
        Student stu1 = new Student("Tweek",15,100);
        Student stu2 = new Student("Jim",15,87);
        Student stu3 = new Student("Peter",15,88);
        Student stu4 = new Student("Tweek",15,100);
        hs.add(stu1);
        hs.add(stu2);
        hs.add(stu3);
        hs.add(stu4);
        //遍历
        for(Student stu: hs){
​
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
    }
}
​
//此时出现两个相同内容的对象
控制台输出:
Name: Jim  Age: 15  Score: 87
Name: Tweek  Age: 15  Score: 100
Name: Tweek  Age: 15  Score: 100
Name: Peter  Age: 15  Score: 88

代码2:




/**
 * @author Tweek
 */
public class Student {
    private String name;
    private int age;
    private int score;
​
    public Student() {
    }
​
    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
​
    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;
    }
​
    public int getScore() {
        return score;
    }
​
    public void setScore(int score) {
        this.score = score;
    }
    
    //以Student中的name比较hashCode
    @Override
    public int hashCode() {
        return name.hashCode();
    }
​
    
    //重写equals的比较内容,以Student对象中的name比较
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Student){
            Student stu = (Student)obj;
            return name.equals(stu.name);
        }
        return super.equals(obj);
    }
}
​
public class HashCodeDemo2 {
    /**
     * 要保证元素唯一性,需要重写hashCode()和equals()方法
     */
    public static void main(String[] args) {
        //创建集合
        HashSet<Student> hs = new HashSet<>();
        Student stu1 = new Student("Tweek",15,100);
        Student stu2 = new Student("Jim",15,87);
        Student stu3 = new Student("Peter",15,88);
        Student stu4 = new Student("Tweek",15,100);
        hs.add(stu1);
        hs.add(stu2);
        hs.add(stu3);
        hs.add(stu4);
        //遍历
        for(Student stu: hs){
​
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
    }
}
​
//没有相同内容的不同对象
控制台输出:
Name: Peter  Age: 15  Score: 88
Name: Tweek  Age: 15  Score: 100
Name: Jim  Age: 15  Score: 87

6、LinkedHashCodeSet集合概述和特点

(1)概述

LinkedHashSet继承于HashSet且是Set的实现类,而且其底层数据结构是哈希表与链表,其具有可预测的迭代次序,换句话说,就是类似于FIFO队列,存储与取出的顺序一致。

(2)LinkedHashSet集合特点

1.哈希表和链表实现的Set接口,具有可预测的迭代次序

2.由链表保证元素有序,也就是说元素的存储和取出顺序是一致的

3.由哈希表保证元素的唯一性,也就是说集合内没有重复的元素

下面的代码示例,能让我们更好地了解LinkedHashSet集合的特点




/**
 * @author Tweek
 */
public class LinkedHashSetDemo1 {
    /**
     * 1.哈希表和链表实现的Set接口,具有可预测的迭代次序
     *
     * 2.由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
     *
     * 3.由哈希表保证元素的唯一性,也就是说集合内没有重复的元素
     *
     */
    public static void main(String[] args) {
        //创建集合
        LinkedHashSet<String> lhs = new LinkedHashSet<>();
        lhs.add("Tweek");
        lhs.add("Jim");
        lhs.add("Louis");
        //不包含重复的元素
        lhs.add("Tweek");
        //遍历
        for(String s : lhs){
            System.out.println(s);
        }
    }
}
​
控制台输出:
Tweek
Jim
Louis

7、TreeSet集合概述和特点

(1)概述

TreeSet集合是一个具体继承自AbstractSet,而且TreeSet实现了Set接口的实现类,即间接地实现了Set接口。在TreeSet内的元素使用自然排序natural Order(自然排序指字母表排序、阿拉伯数字排序)或比较器排序Comparator,使用哪种排序,取决于所使用的构造方法。

(2)TreeSet集合特点

1.元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于所使用的的构造方法,如下表

构造方法说明
TreeSet()根据其元素的自然排序进行排序
TreeSet(Comparator comparator)根据指定的比较器进行排序

2.没有带索引的方法,所以不能使用普通for循环遍历

3.由于是Set集合,所以不包含重复元素的集合

下面的代码示例,能让我们更好地了解TreeSet集合的特点




/**
 * @author Tweek
 */
public class TreeSetDemo1 {
    /**
     * 1.元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于所使用的的构造方法
     *
     * 2.没有带索引的方法,所以不能使用普通for循环遍历
     *
     * 3.由于是Set集合,所以不包含重复元素的集合
     */
    public static void main(String[] args) {
        //自然排序
        //创建集合
        TreeSet<Integer> ts = new TreeSet<Integer>();
        //添加元素
        ts.add(10);
        ts.add(50);
        ts.add(40);
        ts.add(20);
        ts.add(30);
        //不包含重复元素
        ts.add(50);
        //用增强for遍历
        for(int i : ts){
            System.out.println(i);
        }
    }
}
​
控制台输出:
10
20
30
40
50

(3)自然排序Comparable的使用

概述:Comparable接口可以对实现他的每个类的对象强加一个自然排序,故如果某个类要使用自然排序那么就得实现Comparable接口并重写compareTo方法按照需求重写其内容。

要求:存储学生对象并遍历,创建TreeSet集合使用无参构造方法,并按照年龄从小到大排序,年龄相同时按照姓名的字母顺序排序

下面是代码示例:




/**
 * @author Tweek
 */
public class Student implements Comparable<Student>{
    private String name;
    private int age;
    private int score;
​
    public Student() {
    }
​
    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
​
    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;
    }
​
    public int getScore() {
        return score;
    }
​
    public void setScore(int score) {
        this.score = score;
    }
​
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Student student = (Student) o;
        return age == student.age && score == student.score && Objects.equals(name, student.name);
    }
​
    @Override
    public int hashCode() {
        return Objects.hash(name, age, score);
    }
​
    @Override
    public int compareTo(Student s) {
       // return 0; 判断为两值相等
       //    return 1; 判断为升序
        //return -1; 判断为降序
        //升序
            int num = this.age -  s.age;
            //判断年龄相同时,排序按名字字母自然排序
            int num2 = num==0?this.name.compareTo(s.name):num;
            return num2;
    }
}
​
public class TreeSetDemo2 {
    /**
     * 要求:存储学生对象并遍历,创建TreeSet集合使用无参构造方法,并按照年龄从小到大排序,年龄相同时按照姓名的字母顺序排序
     */
    public static void main(String[] args) {
        //创建集合
        TreeSet<Student> ts = new TreeSet<Student>();
        Student stu1 = new Student("Tweek",15,100);
        Student stu2 = new Student("Jim",15,87);
        Student stu3 = new Student("Peter",15,88);
        Student stu4 = new Student("Louis",16,77);
        //添加对象
        ts.add(stu1);
        ts.add(stu2);
        ts.add(stu3);
        ts.add(stu4);
        //遍历
        for (Student stu : ts){
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
    }
}
​
控制台输出:
Name: Jim  Age: 15  Score: 87
Name: Peter  Age: 15  Score: 88
Name: Tweek  Age: 15  Score: 100
Name: Louis  Age: 16  Score: 77

(4)比较器比较Comparator的使用

概述:我们在使用Comparator接口时,应该明确的一点是我们需要Comparator接口的实现类对象,那么此时我们可以使用匿名内部类的方法,来使用Comparator的实现类对象。

要求:存储学生对象并遍历,并按照年龄从小到大排序,年龄相同时按照姓名的字母顺序排序

下面是代码示例:




/**
 * @author Tweek
 */
public class Student {
    private String name;
    private int age;
    private int score;
​
    public Student() {
    }
​
    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
​
    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;
    }
​
    public int getScore() {
        return score;
    }
​
    public void setScore(int score) {
        this.score = score;
    }
​
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Student student = (Student) o;
        return age == student.age && score == student.score && Objects.equals(name, student.name);
    }
​
    @Override
    public int hashCode() {
        return Objects.hash(name, age, score);
    }
}
​
public class TreeSetDemo2 {
    /**
     * 要求:存储学生对象并遍历,并按照年龄从小到大排序,年龄相同时按照姓名的字母顺序排序
     */
    public static void main(String[] args) {
        //创建集合,使用匿名内部来使用Comparator
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                int num = s1.getAge()-s2.getAge();
                int num2 = num==0?s1.getName().compareTo(s2.getName()):num;
                return num2;
            }
        });
        Student stu1 = new Student("Tweek",15,100);
        Student stu2 = new Student("Jim",15,87);
        Student stu3 = new Student("Peter",15,88);
        Student stu4 = new Student("Louis",16,77);
        //添加对象
        ts.add(stu1);
        ts.add(stu2);
        ts.add(stu3);
        ts.add(stu4);
        //遍历
        for (Student stu : ts){
            System.out.println("Name: "+stu.getName()+"  Age: " +
                    stu.getAge()+
                    "  Score: "+stu.getScore());
        }
    }
}
​
控制台输出:
Name: Jim  Age: 15  Score: 87
Name: Peter  Age: 15  Score: 88
Name: Tweek  Age: 15  Score: 100
Name: Louis  Age: 16  Score: 77

五、泛型

1、泛型的概述和好处

(1)概述

泛型:是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译到非法的类型。它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

参数化类型如何理解呢?

顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型

这种参数类型可以用在类、方法、接口中,分别被称为泛型类泛型方法泛型接口

泛型定义格式

1.<类型>:指定一种类型的格式,这里的类型可以看成是形参

2.<类型1,类型2....>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参。

3.将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型。

(2)泛型的好处

1.把运行期的问题提前到了编译时间

2.避免了强制类型转换

下面的两段代码比较,能让我们更好得理解泛型




/**
 * @author Tweek
 */
public class GenericDemo1 {
    /**
     * Collection集合存储字符串并遍历
     */
    public static void main(String[] args) {
        //创建集合对象
        //此时没有限定集合的参数类型,即此时集合的参数类型为Object
        Collection c = new ArrayList();
        //添加元素 
        c.add("Tweek");
        c.add("Jim");
        c.add("Louis");
        c.add(100);
        //遍历集合
        for(Object o : c){
            System.out.println(o);
        }
    }
}
​
控制台输出:
Tweek
Jim
Louis
100

代码2:




/**
 * @author Tweek
 */
public class GenericDemo1 {
    /**
     * Collection集合存储字符串并遍历
     */
    public static void main(String[] args) {
        //创建集合对象
        //此时没有限定集合的参数类型,即此时集合的参数类型为Object
        Collection<String> c = new ArrayList<>();
        //添加元素
        c.add("Tweek");
        c.add("Jim");
        c.add("Louis");
        //java: 不兼容的类型: int无法转换为java.lang.String
        //c.add(100);
        //遍历集合
        for(String s : c){
            System.out.println(s);
        }
    }
}
​
控制台输出:
Tweek
Jim
Louis

2、泛型类

(1)泛型类的定义格式

1.格式:修饰符class 类名<类型>{}

2、范例:public class Generic<T>{}

此处的T可以随便写为任意表示,常见的如T、E、K、V等形式的参数常用于表示泛型。

下面的代码示例,能让我们更好的了解泛型类




/**
 * @author Tweek
 */
public class Student {
    private String name;
    private int age;
    private int score;
​
    public Student() {
    }
​
    public Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
​
    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;
    }
​
    public int getScore() {
        return score;
    }
​
    public void setScore(int score) {
        this.score = score;
    }
}
public class Teacher {
    private String name;
    private String subject;
​
    public Teacher() {
    }
​
    public Teacher(String name, String subject) {
        this.name = name;
        this.subject = subject;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public String getSubject() {
        return subject;
    }
​
    public void setSubject(String subject) {
        this.subject = subject;
    }
}
public class mGeneric<T> {
   private T t ;
​
    public T getT() {
        return t;
    }
​
    public void setT(T t) {
        this.t = t;
    }
}
public class GenericDemo2 {
    public static void main(String[] args) {
        //创建两个对象
          Student stu = new Student("Tweek",15,87);
          Teacher tch = new Teacher("Jim","English");
        System.out.println(stu.getAge());
        System.out.println(tch.getSubject());
        System.out.println("-------------------------------------------");
        //利用一个泛型类
        mGeneric<Integer> g1= new mGeneric<Integer>();
        mGeneric<String> g2= new mGeneric<String>();
        g1.setT(15);
        System.out.println(g1.getT());
        g2.setT("English");
        System.out.println(g2.getT());
    }
}
​
控制台输出:
15
English
-------------------------------------------
15
English

3、泛型方法

(1)泛型方法的定义格式:

1.格式:修饰符<类型>返回值类型方法名(类型 变量名){}

2.范例:public<T> void show(T t){}

下面的代码示例,能让我们更好地了解泛型方法




/**
 * @author Tweek
 */
public class mGeneric<T> {
     public<T> void show(T t){
         System.out.println(t);
     }
}
public class GenericDemo2 {
    public static void main(String[] args) {
​
       mGeneric g = new mGeneric();
       g.show("Tweek");
       g.show(15);
       g.show(true);
       g.show(15.15);
​
    }
}
​
控制台输出:
Tweek
15
true
15.15

4、泛型接口

(1)泛型接口的定义格式

1.格式:修饰符interface 接口名<类型>{ }

2.范例:public interface Generic<T>{ }



/**
 * @author Tweek
 */
public interface mGenericInt<T> {
    void show(T t);
}
public class mGenericImp<T> implements mGenericInt<T>{
​
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}
public class GenericDemo2 {
    public static void main(String[] args) {
​
       mGenericImp g = new mGenericImp();
       g.show("Tweek");
       g.show(15);
       g.show(true);
       g.show(15.15);
​
    }
}
​
控制台输出:
Tweek
15
true
15.15

5、类型通配符

(1)类型通配符概述

为了表示各种泛型List的父类,可以使用类型通配符

1.类型通配符: <?>

2.List<?> : 表示元素类型未知的List,它的元素可以匹配任何类型

3.这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中

如果说我们不希望List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父类,可以使用类型通配符的上限

4.类型通配符的上限:<? extends 类型>

5.List<? extends Number>:表示的类型是Number或者其子类型

除了可以指定类型通配符的上限,我们还可以指定类型通配符的下限

6.类型通配符的下限:<? super 类型>

7.List<? super Number>:表示的类型是Number或者其父类型

下面的代码,能让我们更好地理解类型通配符




/**
 * @author Tweek
 */
public class GenericDemo3 {
    public static void main(String[] args) {
        //类型通配符,Object是Number的父类,Number是Integer的父类
        List<?> ls1 = new ArrayList<Object>();
        List<?> ls2 = new ArrayList<Number>();
        List<?> ls3 = new ArrayList<Integer>();
        System.out.println("----------");
        //类型通配符上限:<? extends 类型>
        //Number的上限是它自己,所以不能创建Object类型对象
        //List<? extends Number> ls4 = new ArrayList<Object>();
        List<? extends Number> ls5 = new ArrayList<Number>();
        List<? extends Number> ls6 = new ArrayList<Integer>();
        System.out.println("----------");
        //类型通配符下限:<? super 类型>
        //Number的下限是它自己,所以不能创建其子类Integer类型的对象
        List<? super Number> ls7 =new ArrayList<Object>();
        List<? super Number> ls8 =new ArrayList<Number>();
        //List<? super Number> ls9 =new ArrayList<Integer>();
    }
}

6、可变参数

(1)格式:修饰符 返回值类型 方法名(数据类型...变量名){ }

(2)范例:public static int sum(int... a){ }

对于int... a 其实是把方法的数据封装成了一个数组a

(3)注意事项:

1.这里的变量其实是一个数组

2.如果一个方法有多个参数,包含可变参数,可变参数要放在最后

下面的代码示例,能让我们更好地了解可变参数:




/**
 * @author Tweek
 */
public class ArgsDemo1 {
    public static void main(String[] args) {
        System.out.println(sum(10,20));
        System.out.println(sum(10,20,30));
        System.out.println(sum(10,20,30,40));
        System.out.println(sum(10,20,30,40,50,60));
        System.out.println(sum(10,20,30,40,50,60,70));
    }
​
    private static int sum(int... a) {
        int sum=0;
        System.out.println(a);
        for(int i : a){
            sum+=i;
        }
        return sum;
    }
}
​
控制台输出:
30
60
100
210
280

7、可变参数的使用

注:这list.of是JDK9的新特性

方法名说明
Arrays工具类中有一个静态方法(返回的集合不能做增删操作,可以做修改操作)
public static <T> List<T> asList(T... a)返回指定数组支持的固定大小的列表
List接口中有一个静态方法(返回的集合不能做增删改操作)
public static <E> List <E> of(E... elements)返回包含任意数量元素的不可变列表
Set接口中有一个静态方法(返回的集合不能做增删操作,没有修改的方法)
public static <E> Set <E> of(E... elements)返回一个包含任意数量元素的不可变集合

六、Map集合

1、Map集合的概述与特点

(1)概述

Map集合是一个接口,其形式是Map<K、V>,其中K是Map里面存储的键的类型V是Map里面存储的值的类型。Map接口是一个将键K映射到值V的对象,Map中不能包含重复的键K,每个键K可以映射最多一个值V

创建Map集合的对象:采用多态的方式通过具体的实现类HashMap来创建Map集合的对象

(2)特点

1、Map集合中不包含重复的元素,在出现两个相同K,但是不相同V的时候,Map集合会将相同K中的旧元素删去,更新K对应的V是新元素。

2、Map集合的输出顺序,不是按存储顺序输出。

下面的代码示例,能让我们更好地了解Map集合




/**
 * @author Tweek
 */
public class MapDemo1 {
    /**
     * Map集合
     * @param args
     */
    public static void main(String[] args) {
      //创建集合对象
        Map<String,String> mp = new HashMap<String , String >();
        //V put(K key , V value) 将指定的值与该映射中的指定键相关联
        mp.put("ID1515","Tweek");
        mp.put("ID1516","Jim");
        mp.put("ID1517","Peter");
        //不包含重复元素。若是相同的K,但是不同的内容,Map集合会将相同K中的旧元素删去,更新元素
        mp.put("ID1517","Louis");
​
        //输出集合
        System.out.println(mp);
    }
}
​
控制台输出:
{ID1517=Louis, ID1516=Jim, ID1515=Tweek}

2、Map集合的基本功能

方法名说明
Vput(K key,V value)添加元素
V remove(Object key)根据键删除键值对元素
void clear()移除所有的键值对元素
boolean containKey(Object key)判断集合是否包含指定的键
bool containsValue(Object value)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中键值对的个数

下面的代码示例,能让我们更好地了解Map集合的基本功能




/**
 * @author Tweek
 */
public class MapDemo2 {
    /**
     * | V put(K key,V value)              | 添加元素                             |
     * | V remove(Object key)             | 根据键删除键值对元素                 |
     * | void clear()                     | 移除所有的键值对元素                 |
     * | boolean containKey(Object key)   | 判断集合是否包含指定的键             |
     * | bool containsValue(Object value) | 判断集合是否包含指定的值             |
     * | boolean isEmpty()                | 判断集合是否为空                     |
     * | int size()                       | 集合的长度,也就是集合中键值对的个数 |
     */
    public static void main(String[] args) {
        //创建集合
        Map<String,String> map = new HashMap<>();
        //1.V put(K key,V value)
        System.out.print("添加元素:");
        map.put("ID1","Tweek");
        map.put("ID2","Jim");
        map.put("ID3","Peter");
        map.put("ID4","Meg");
        System.out.println(map);
        System.out.println("----------------------------");
        //2.V remove(Object key)
        System.out.print("移除元素前:");
        System.out.println(map);
        System.out.print("移除ID4的"+map.remove("ID4")+"元素后:");
        System.out.println(map);
        System.out.println("----------------------------");
        //3.boolean containKey(Object key)
        System.out.println("判断是否存在这个Key值:");
        System.out.println("是否存在ID1这个Key?true存在,false不存在");
        System.out.println("回答: "+map.containsKey("ID1"));
        System.out.println("是否存在ID5这个Key?true存在,false不存在");
        System.out.println("回答: "+map.containsKey("ID5"));
        System.out.println("----------------------------");
        //4.bool containsValue(Object value)
        System.out.println("判断是否存在这个Value值:");
        System.out.println("是否存在Tweek这个Value?true存在,false不存在");
        System.out.println("回答: "+map.containsValue("Tweek"));
        System.out.println("是否存在Meg这个Value?true存在,false不存在");
        System.out.println("回答: "+map.containsValue("Meg"));
        System.out.println("----------------------------");
        //5.boolean isEmpty()
        System.out.println("判断该集合是否为空");
        System.out.println("为空true,不为空则false");
        System.out.println("回答:"+map.isEmpty());
        System.out.println("查看集合:"+map);
        System.out.println("----------------------------");
        //6.int size()
        System.out.println("该集合有多长?");
        System.out.println("回答:长度为"+map.size());
        System.out.println("----------------------------");
        //7. void clear()
        System.out.println("清空集合前:"+map);
        map.clear();
        System.out.println("判断集合是否为空:isEmpty?"+map.isEmpty());
        System.out.println("清空集合后:"+map);
    }
}
​
控制台输出:
添加元素:{ID2=Jim, ID1=Tweek, ID4=Meg, ID3=Peter}
----------------------------
移除元素前:{ID2=Jim, ID1=Tweek, ID4=Meg, ID3=Peter}
移除ID4的Meg元素后:{ID2=Jim, ID1=Tweek, ID3=Peter}
----------------------------
判断是否存在这个Key值:
是否存在ID1这个Key?true存在,false不存在
回答: true
是否存在ID5这个Key?true存在,false不存在
回答: false
----------------------------
判断是否存在这个Value值:
是否存在Tweek这个Value?true存在,false不存在
回答: true
是否存在Meg这个Value?true存在,false不存在
回答: false
----------------------------
判断该集合是否为空
为空true,不为空则false
回答:false
查看集合:{ID2=Jim, ID1=Tweek, ID3=Peter}
----------------------------
该集合有多长?
回答:长度为3
----------------------------
清空集合前:{ID2=Jim, ID1=Tweek, ID3=Peter}
判断集合是否为空:isEmpty?true
清空集合后:{}

3、Map集合的获取功能

方法名说明
V get(Object key)根据键获取值
Set<K> keySet()获取所有键的集合
Collection<V> values()获取所有值的集合
Set<Map.Enty<K,V>> entrySet()获取所有键值对对象的集合

下面的代码示例,能让我们更好地了解Map集合的获取功能




/**
 * @author Tweek
 */
public class MapDemo3 {
    /**
     * | V get(Object key)             | 根据键获取值             |
     * | Set<K> keySet()               | 获取所有键的集合         |
     * | Collection<V> values()        | 获取所有值的集合         |
     * | Set<Map.Enty<K,V>> entrySet() | 获取所有键值对对象的集合 |
     */
    public static void main(String[] args) {
        //创建集合
        Map<String,String> map = new HashMap<>();
        //添加元素
        map.put("ID1","Tweek");
        map.put("ID2","Jim");
        map.put("ID3","Peter");
        map.put("ID4","Meg");
        System.out.println(map);
        System.out.println("----------------------------");
        //1.根据键获取value,如果对应的键值不存在,则值返回null
        System.out.println("根据键获取value");
        System.out.println(map.get("ID1"));
        System.out.println(map.get("ID5"));
        //2.获取所有键的集合
        System.out.println("获取所有键的集合");
        //创建集合
        Set<String> keySet = map.keySet();
        for(String s : keySet){
            System.out.println(s);
        }
        System.out.println("----------------------------");
        //3.获取所有值的集合
        System.out.println("获取所有值的集合");
        //创建集合
        Collection<String> valueSet = map.values();
        for(String s:valueSet){
            System.out.println(s);
        }
        System.out.println("----------------------------");
        //4.获取所有键值对对象的集合
        System.out.println("获取所有键值对的集合");
        //创建集合
        Set<Map.Entry<String,String>> entrySet =  map.entrySet();
        for(Map.Entry<String,String> s:entrySet){
            System.out.println(s);
        }
    }
}
​
控制台输出:
{ID2=Jim, ID1=Tweek, ID4=Meg, ID3=Peter}
----------------------------
根据键获取value
Tweek
null
获取所有键的集合
ID2
ID1
ID4
ID3
----------------------------
获取所有值的集合
Jim
Tweek
Meg
Peter
----------------------------
获取所有键值对的集合
ID2=Jim
ID1=Tweek
ID4=Meg
ID3=Peter

4、Map的遍历方法

方式一

获取所有键的集合,用keySet()方法实现

(1)用增强for循环遍历

1.遍历键的集合,获取到每一个键,用增强for实现

2.根据键去找值,用get(Object key)来根据键获取值

(2)用迭代器遍历

1.遍历键的集合,获取每一个键,用迭代器的方法来实现

2.根据键去找值,用get(Object key)来根据键获取值

下面的代码示例,能让我们更好地了解Map遍历方法



/**
 * @author Tweek
 */
public class MapDemo4 {
    public static void main(String[] args) {
        /**
         * Map的遍历方法
         * 1.获取所有键的集合,用keySet()方法实现
         *
         * (1)遍历键的集合,获取到每一个键,用增强for实现
         *
         * (2)根据键去找值,用get(Object key)来根据键获取值
         * 2.用迭代器遍历
         */
        //创建集合
        Map<String,String> map = new HashMap<>();
        //添加元素
        map.put("ID1","Tweek");
        map.put("ID2","Jim");
        map.put("ID3","Peter");
        map.put("ID4","Meg");
        //1.获取所有键的集合,用KeySet方法实现
        Set<String> keySet = map.keySet();
        for(String s : keySet){
            String key = s;
            String value = map.get(key);
            System.out.println(key+"="+value);
        }
        System.out.println("----------------------");
        //2.用迭代器遍历
        Set<String> itkey = map.keySet();
        Iterator<String> it = itkey.iterator();
        while(it.hasNext()){
            String key = it.next();
            String value = map.get(key);
            System.out.println(key+"="+value);
        }
    }
}
​
控制台输出:
ID2=Jim
ID1=Tweek
ID4=Meg
ID3=Peter
----------------------
ID2=Jim
ID1=Tweek
ID4=Meg
ID3=Peter

方式二

根据键值对对象来找键和值

(1)用增强for来遍历

1.获取所有的键值对的集合

2.遍历包含所有键值对的Set集合,得到每一个键值对对象

3.根据获取到的每一个键值对,来获取键和值

(2)用迭代器遍历

1.获取所有的键值对的集合

2.遍历包含所有键值对的Set集合,得到每一个键值对对象

3.根据获取到的每一个键值对,来获取键和值

下面的代码示例,能让我们更好地了解Map遍历方法




/**
 * @author Tweek
 */
public class MapDemo5 {
    /**
     * 根据键值对对象来找键和值
     *
     * (1)用增强for来遍历
     *
     *  1.获取所有的键值对的集合
     *
     *  2.遍历包含所有键值对的Set集合,得到每一个键值对对象
     *
     *  3.根据获取到的每一个键值对,来获取键和值
     *
     * (2)用迭代器遍历
     */
    public static void main(String[] args) {
        //创建集合
        Map<String,String> map = new HashMap<String,String>();
        //添加元素
        map.put("ID1","Tweek");
        map.put("ID2","Jim");
        map.put("ID3","Peter");
        map.put("ID4","Meg");
        //遍历集合
        Set<Map.Entry<String,String>>  entrySet = map.entrySet();
        //1.增强for
        for(Map.Entry<String,String> s : entrySet){
            String key = s.getKey();
            String value = s.getValue();
            System.out.println(key+"="+value);
        }
        System.out.println("----------------------");
        //2.迭代器遍历
        Iterator<Map.Entry<String,String>> it =entrySet.iterator();
        while(it.hasNext()){
            Map.Entry<String,String> entry=it.next();
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key+"="+value);
​
        }
    }
}
​
控制台输出:
ID2=Jim
 

ID1=Tweek
ID4=Meg
ID3=Peter
----------------------
ID2=Jim
ID1=Tweek
ID4=Meg
ID3=Peter

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值