JAVA——集合的使用

目录

一、Collection集合

1.集合概述

2.集合框架

3.Collection 常用功能

二、Iterator迭代器

1.Iterator接口

2.迭代器的实现原理

3.增强for

(1)遍历数组

(2)遍历集合

三、List集合

1.List接口介绍

2.List接口中常用方法

四、List的子类

1.ArrayList集合

2.LinkedList集合

3.Vector类

五、Set接口

1.HashSet集合

2.HashSet集合存储数据的结构

(1)哈希值

(2)哈希表  

3.HashSet存储自定义类型元素

4.LinkedHashSet

5.可变参数

六、Collections

1.常用功能

2.Comparator比较器

3.Comparable和Comparator的区别

4.练习

5.扩展

七、Map集合

1.概述

2.Map常用子类

3.Map接口中的常用方法

4.Map集合遍历键找值方式

​5.Entry键值对对象

6.Map集合遍历键值对方式

7.HashMap存储自定义类型键值

8.LinkedHashMap

9.HashTable

10.Map集合练习

八、模拟斗地主洗牌发牌

1.介绍

2. 需求分析

3.实现代码


一、Collection集合

1.集合概述

集合:集合是java中提供的一种容器,可以用来存储多个数据。

集合和数组都是容器其区别如下:

a.数组的长度是固定的。集合的长度是可变的。

b.数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象。而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。

2.集合框架

        Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是java.util.Listjava.util.Set。其中,List的特点是元素有序、元素可重复。Set的特点是元素无序,而且不可重复。List接口的主要实现类有java.util.ArrayListjava.util.LinkedListSet接口的主要实现类有java.util.HashSetjava.util.TreeSet

其中,橙色框里填写的都是接口类型,而蓝色框里填写的都是具体的实现类。

集合本身是一个工具,它存放在java.util包中。在Collection接口定义着单列集合框架中最最共性的内容。

3.Collection 常用功能

        Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。方法如下:

a.public boolean add(E e): 把给定的对象添加到当前集合中 。

b.public void clear() :清空集合中所有的元素。

c.public boolean remove(E e): 把给定的对象在当前集合中删除。

d.public boolean contains(E e): 判断当前集合中是否包含给定的对象。

e.public boolean isEmpty(): 判断当前集合是否为空。

f.public int size(): 返回集合中元素的个数。

g.public Object[] toArray(): 把集合中的元素,存储到数组中。

方法演示:

import java.util.ArrayList;
import java.util.Collection;

public class Test {
    public static void main(String[] args) {
		// 创建集合对象 
    	// 使用多态形式
    	Collection<String> coll = new ArrayList<String>();
    	// 使用方法
    	// 添加功能  boolean  add(String s)
    	coll.add("张三");
    	coll.add("李四");
    	coll.add("王五");
    	System.out.println(coll);
    	
    	System.out.println("判断张三是否在集合中"+coll.contains("张三"));

    	System.out.println("删除李四:"+coll.remove("李四"));
    	System.out.println("操作之后集合中元素:"+coll);
    	
    	// size() 集合中有几个元素
		System.out.println("集合中有"+coll.size()+"个元素");

		// Object[] toArray()转换成一个Object数组
    	Object[] objects = coll.toArray();
    	// 遍历数组
    	for (int i = 0; i < objects.length; i++) {
			System.out.println(objects[i]);
		}

		// void  clear() 清空集合
		coll.clear();
		System.out.println("集合中内容为:"+coll);
		// boolean  isEmpty()  判断是否为空
		System.out.println(coll.isEmpty());  	
	}
}

二、Iterator迭代器

 1.Iterator接口

        在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.IteratorIterator接口也是Java集合中的一员,但它与CollectionMap接口有所不同,Collection接口与Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象也被称为迭代器。  

想要遍历Collection集合,那么就要获取该集合迭代器完成迭代操作,下面介绍一下获取迭代器的方法:

public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素的。

迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

Iterator接口的常用方法如下:

a.public E next():返回迭代的下一个元素。

b.public boolean hasNext():如果仍有元素可以迭代,则返回 true。

public class Test {
  	public static void main(String[] args) {
        // 使用多态方式创建对象
        Collection<String> coll = new ArrayList<String>();
        // 添加元素到集合
        coll.add("张三");
        coll.add("李四");
        coll.add("王五");
        //使用迭代器遍历,每个集合对象都有自己的迭代器
        Iterator<String> it = coll.iterator();
        while(it.hasNext()){ //判断是否有迭代元素
            String s = it.next();//获取迭代出的元素
            System.out.println(s);
        }
  	}
}

tips::在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会发生java.util.NoSuchElementException没有集合元素的错误。  

2.迭代器的实现原理

        当遍历集合时,首先通过调用集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素,下面通过一个图例来演示Iterator对象迭代元素的过程:

在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。  

3.增强for

        增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。格式如下:

for(元素的数据类型  变量 : Collection集合or数组){ 
      //写操作代码
}

它用于遍历Collection和数组。通常只进行遍历元素,不要在遍历的过程中对集合元素进行增删操作。  

(1)遍历数组

public class Test {
    public static void main(String[] args) {
		int[] arr = {3,5,6,87};
       	//使用增强for遍历数组
		for(int a : arr){//a代表数组中的每个元素
			System.out.println(a);
		}
	}
}

(2)遍历集合

public class Test {
    public static void main(String[] args) {        
    	Collection<String> coll = new ArrayList<String>();
    	coll.add("张三");
    	coll.add("李四");
    	coll.add("王五");
    	//使用增强for遍历
    	for(String s :coll){
    		System.out.println(s);
    	}
	}
}

 tips: 新for循环必须有被遍历的目标。目标只能是Collection或者是数组。新式for仅仅作为遍历操作出现。

三、List集合

1.List接口介绍

   java.util.List接口继承自Collection接口,是单列集合的一个重要分支,习惯性地会将实现了List接口的对象称为List集合。在List集合中允许出现重复的元素,所有的元素是以一种线性方式进行存储的,在程序中可以通过索引来访问集合中的指定元素。另外,List集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致。         

List接口特点:

a.它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。

b.它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。

c.集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。

2.List接口中常用方法

        List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法,如下:

a.public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。

b.public E get(int index):返回集合中指定位置的元素。

c.public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。

d.public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。

public class Test {
    public static void main(String[] args) {
		// 创建List集合对象
    	List<String> list = new ArrayList<String>();
    	
    	// 往 尾部添加 指定元素
    	list.add("张三");
    	list.add("李四");
    	list.add("王五");
    	
    	System.out.println(list);
    	// add(int index,String s) 往指定位置添加
    	list.add(1,"没头脑");
    	
    	System.out.println(list);
    	// String remove(int index) 删除指定位置元素  返回被删除元素
    	// 删除索引位置为2的元素 
    	System.out.println("删除索引位置为2的元素");
    	System.out.println(list.remove(2));
    	
    	System.out.println(list);
    	
    	// String set(int index,String s)
    	// 在指定位置 进行 元素替代(改) 
    	// 修改指定位置元素
    	list.set(0, "三毛");
    	System.out.println(list);
    	
    	// String get(int index)  获取指定位置元素
    	
    	// 跟size() 方法一起用  来 遍历的 
    	for(int i = 0;i<list.size();i++){
    		System.out.println(list.get(i));
    	}
    	//还可以使用增强for
    	for (String string : list) {
			System.out.println(string);
		}  	
	}
}

四、List的子类

 1.ArrayList集合

    java.util.ArrayList集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        //使用多态的方式创建ArrayList实现类的方式使用
        List<String> list = new ArrayList<>();
        //集合中添加元素
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("a");
        //打印集合
        System.out.println(list);

        //在c和a之间添加一个java
        list.add(3,"java");
        System.out.println(list);
        System.out.println("=============");
        
        String removeE = list.remove(2);
        System.out.println("被移除的元素是:"+removeE);
        System.out.println(list);
       
        //遍历集合
        for (int i =0;i<list.size();i++){
            System.out.println(list.get(i));
        }
        System.out.println("==========");
        //迭代器方式取出内容
        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
        System.out.println("===增强for循环==");
 
        for (String str: list){
            System.out.println(str);
        }
        String  str = list.get(5);
        System.out.println(str);

    }

}

2.LinkedList集合

      java.util.LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。

实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。方法如下:

a.public void addFirst(E e):将指定元素插入此列表的开头。

b.public void addLast(E e):将指定元素添加到此列表的结尾。

c.public E getFirst():返回此列表的第一个元素。

d.public E getLast():返回此列表的最后一个元素。

e.public E removeFirst():移除并返回此列表的第一个元素。

f.public E removeLast():移除并返回此列表的最后一个元素。

g.public E pop():从此列表所表示的堆栈处弹出一个元素。

h.public void push(E e):将元素推入此列表所表示的堆栈。

i.public boolean isEmpty():如果列表不包含元素,则返回true。

LinkedList是List的子类,List中的方法LinkedList都是可以使用,我们只需要了解LinkedList的特有方法即可。

注意:使用LinkedList集合的特有方法,不能使用多态。

LinkedList集合的特点:

a.底层是链表结构:查询慢,增删快。

b.里面包含了大量操作首位元素的方法。

ArrayList和LinkedList的区别:

a.ArrayList是基于数组的数据结构存储数据的, 查询快,根据索引首地址进行查询。

b.LinkedList是基于链表的数据结构存储数据的, 查询慢,增删快。

import java.util.LinkedList;


public class Test {

    public static void main(String[] args) {
       show01();

       show02();

       show03();

    }

    private static void show01() {
        LinkedList<String>  linked = new LinkedList();
        //使用add方法放入内容
        linked.add("a");
        linked.add("b");
        linked.add("c");
        System.out.println(linked);

        String first = linked.removeFirst();
        System.out.println("被移除的第一个元素:" + first);
        System.out.println("移除最后一个元素:"+linked.removeLast());


    }

    private static void show02() {
        LinkedList<String>  linked = new LinkedList();
        //使用add方法放入内容
        linked.add("a");
        linked.add("b");
        linked.add("c");
       
        if (!linked.isEmpty()){
            System.out.println(linked.getFirst());
        }

    }

    private static void show03() {
        LinkedList<String>  linked = new LinkedList();
        //使用add方法放入内容
        linked.add("a");
        linked.add("b");
        linked.add("c");
        System.out.println(linked);
        System.out.println("==============");
        
        linked.push("java");
        linked.addLast("doyens");
        System.out.println(linked);
        System.out.println("=========");
    }

}

3.Vector类

    Vector 类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。

        每个向量会试图通过维护 capacitycapacityIncrement 来优化存储管理。capacity 始终至少应与向量的大小相等;这个值通常比后者大些,因为随着将组件添加到向量中,其存储将按 capacityIncrement 的大小增加存储块。应用程序可以在插入大量组件前增加向量的容量;这样就减少了增加的重分配的量。

        由 Vector 的 iterator 和 listIterator 方法所返回的迭代器是快速失败的:如果在迭代器创建后的任意时间从结构上修改了向量(通过迭代器自身的 remove 或 add 方法之外的任何其他方式),则迭代器将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就完全失败,而不是冒着在将来不确定的时间任意发生不确定行为的风险。Vector 的 elements 方法返回的 Enumeration 不是 快速失败的。

五、Set接口

    java.util.Set接口和java.util.List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格了。与List接口不同的是,Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。Set集合有多个子类,此处介绍其中的java.util.HashSetjava.util.LinkedHashSet这两个集合。

Set 接口特点:

a.不允许存储重复的元素。

b.没有索引,没有带索引的方法,也不能使用普通的for循环遍历。

tips:Set集合取出元素的方式可以采用:迭代器、增强for。

 1.HashSet集合

    java.util.HashSetSet接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。java.util.HashSet底层的实现其实是一个java.util.HashMap支持。HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCodeequals方法。

HashSet特点:

a.不允许存储重复的元素。

b.没有索引,没有带索引的方法,也不能使用普通的for循环遍历。

c.是一个无序的集合,存储和取出元素的顺序不一样。

d.底层是一个哈希表结构(查询速度非常快)。

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Test {

    public static void main(String[] args) {
        Set<Integer>  set = new HashSet<>();
        //使用add方法往集合中添加元素
        set.add(1);
        set.add(3);
        set.add(2);
        set.add(1);
        System.out.println(set);
        //使用迭代器遍历里面的数据
        Iterator<Integer> it = set.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
        System.out.println("=============");
        //使用增强for循环
        for(Integer l: set){
            System.out.println(l);
        }
    }
}

2.HashSet集合存储数据的结构

 (1)哈希值

        哈希值值是一个十进制的整数,由系统随机给出(就是对象地址值,是一个逻辑地址,是模拟出来的地址,不是数据实际存储的物理地址),在Object类中有一个方法可以获取对象的hash值。int hashCode()返回该对象的哈希值。

hashCode源码:

public native int hashCode();

​ native:代表该方法是调用的本地操作系统的方法。

public class Person {

    //重写hashCode方法
   / *
    @Override
    public int hashCode() {
        return 1;
    }
    */
}
public class Test {
    public static void main(String[] args) {
        Person p1 = new Person();
        int h1 = p1.hashCode();
        System.out.println(h1);

        Person p2 = new Person();
        int h2 = p2.hashCode();
        System.out.println(h2);

        /*
           toString方法的源码
         */
        System.out.println(p1);
        System.out.println(p2);
        
        //String类也重写了hashcode方法
        String s1  ="abc";
        String s2  ="abc";
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());

        String s3 = "张三";
        String s4 = "李四";
        System.out.println(s3.hashCode());   
        System.out.println(s4.hashCode());

    }
}

(2)哈希表  

        在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示:

总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。

3.HashSet存储自定义类型元素

        给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一。

public class Student {
    private String name;
    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        Student student = (Student) o;
        return age == student.age &&
               Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
public class Test {
    public static void main(String[] args) {
        //创建集合对象   该集合中存储Student类型对象
        HashSet<Student> stuSet = new HashSet<Student>();
        //存储 
        Student stu = new Student("张三", 43);
        stuSet.add(stu);
        stuSet.add(new Student("李四", 44));
        stuSet.add(new Student("王五", 43));
        stuSet.add(new Student("赵六", 23));
        stuSet.add(stu);

        for (Student stu2 : stuSet) {
            System.out.println(stu2);
        }
    }
}

4.LinkedHashSet

        我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,就可以使用在HashSet下面的一个子类java.util.LinkedHashSet,它是链表和哈希表组合的一个数据存储结构。

public class Test {
	public static void main(String[] args) {
		Set<String> set = new LinkedHashSet<String>();
		set.add("bbb");
		set.add("aaa");
		set.add("abc");
		set.add("bbc");
        Iterator<String> it = set.iterator();
		while (it.hasNext()) {
			System.out.println(it.next());
		}
	}
}

5.可变参数

        在JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化成如下格式:

 修饰符 返回值类型 方法名(参数类型... 形参名){  }

其实这个书写完全等价与:

修饰符 返回值类型 方法名(参数类型[] 形参名){  }

只是后面这种定义,在调用时必须传递数组,而前者可以直接传递数据即可。

同样是代表数组,但是在调用这个带有可变参数的方法时,不用创建数组(这就是简单之处),直接将数组中的元素作为实际参数进行传递,其实编译成的class文件,将这些元素先封装到一个数组中,在进行传递。这些动作都在编译.class文件时,自动完成了。

可变参数注意事项:

a.一个方法的参数列表,,只能有一个可变参数。

b.如果方法的参数有多个,那么可变参数必须写到参数列表的末尾。

public class Test {

    public static void main(String[] args) {
        System.out.println(getSum(10,20,12,234,34,45));
    }


    public  static int getSum(int...arr){
       int sum=0;
       for (int i :arr){
           sum += i;
       }
        return sum;
    }

}

六、Collections

Collection和Collections的区别:

a. Collection是单列集合的一个父接口,  Collections只是一个集合工具类,里面操作集合的方法,比如说shuffle(list),还有sort方法,集合中的数据排序, addAll方法,可同时往集合中添加多个元素。

 1.常用功能

java.utils.Collections是集合工具类,用来对集合进行操作。部分方法如下:

a.public static <T> boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素。

b.public static void shuffle(List<?> list) 打乱顺序:打乱集合顺序。

c.public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。

d.public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。

import java.util.ArrayList;
import java.util.Collections;

public class Test {
    public static void main(String[] args) {
        ArrayList<String>  list = new ArrayList<>();
      
        Collections.addAll(list,"a","b","c","d");

        System.out.println(list);
        Collections.shuffle(list);
        System.out.println(list);
    }
}

2.Comparator比较器

 public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。

import java.util.ArrayList;
import java.util.Collections;

public class Test {

    public static void main(String[] args) {

        ArrayList<Integer>  list01 = new ArrayList<>();
        list01.add(1);
        list01.add(3);
        list01.add(2);
        System.out.println(list01);

        Collections.sort(list01);
        System.out.println(list01);
        System.out.println("=================");
        ArrayList<String> list02 = new ArrayList<>();
        list02.add("a");
        list02.add("c");
        list02.add("b");
        System.out.println(list02);

        Collections.sort(list02);
        System.out.println(list02);


        ArrayList<Person>  list03 = new ArrayList<>();
        list03.add(new Person("张三",18));
        list03.add(new Person("李四",28));
        list03.add(new Person("王五",26));
        System.out.println(list03);
        Collections.sort(list03);
        System.out.println(list03);

    }
}

对于排序,简单的说就是两个对象之间比较大小,那么在JAVA中提供了两种比较实现的方式,一种是比较死板的采用java.lang.Comparable接口去实现,一种是灵活的当我需要做排序的时候在去选择的java.util.Comparator接口完成。

那么我们采用的public static <T> void sort(List<T> list)这个方法完成的排序,实际上要求了被排序的类型需要实现Comparable接口完成比较的功能,在String类型上如下所示:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence

String类实现了这个接口,并完成了比较规则的定义,但是这样就把这种规则写死了,那比如我想要字符串按照第一个字符降序排列,那么这样就要修改String的源代码,这是不可能的了,那么这个时候我们可以使用public static <T> void sort(List<T> list,Comparator<? super T> )方法灵活的完成,这个里面就涉及到了Comparator这个接口,位于位于java.util包下,排序是comparator能实现的功能之一,该接口代表一个比较器,比较器具有可比性。顾名思义就是做排序的,通俗地讲需要比较两个对象谁排在前谁排在后,那么比较的方法就是:

 public int compare(String o1, String o2):比较其两个参数的顺序。

两个对象比较的结果有三种:大于,等于,小于。

如果要按照升序排序,则o1 小于o2,返回(负数),相等返回0,01大于02返回(正数)。

如果要按照降序排序,则o1 小于o2,返回(正数),相等返回0,01大于02返回(负数)。

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Test {
    public static void main(String[] args) {
        ArrayList<Integer>  list01 = new ArrayList<>();
        list01.add(1);
        list01.add(3);
        list01.add(2);
        System.out.println(list01);
        Collections.sort(list01, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
              //  return o1 - o2; //升序
                return  o2 - o1; //降序
            }
        });
        System.out.println(list01);
        System.out.println("==============");
        ArrayList<Student>  list02 = new ArrayList<>();
        list02.add(new Student("张三",30));
        list02.add(new Student("李四",18));
        list02.add(new Student("a李四",20));
        list02.add(new Student("a王五",18));

        Collections.sort(list02, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int result = o1.getAge() - o2.getAge();//按照年龄升序排列
                if (result == 0){
                    result = o1.getName().charAt(0) - o2.getName().charAt(0);
                }
                return result;
            }
        });
        System.out.println(list02);
    }
}

3.Comparable和Comparator的区别

a.Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。

b.Comparator:强行对某个对象进行整体排序。可以将Comparator 传递给sort方法(如Collections.sort或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。

 4.练习

 创建一个学生类,存储到ArrayList集合中完成指定排序操作。

Student 初始类:

public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
               "name='" + name + '\'' +
               ", age=" + age +
               '}';
    }

    @Override
    public int compareTo(Student o) {
        return this.age-o.age;//升序
    }
}

测试类:

import java.util.ArrayList;
import java.util.Collections;

public class Test{

    public static void main(String[] args) {
        // 创建四个学生对象 存储到集合中
        ArrayList<Student> list = new ArrayList<Student>();

        list.add(new Student("rose",18));
        list.add(new Student("jack",16));
        list.add(new Student("abc",16));
        list.add(new Student("ace",17));
        list.add(new Student("mark",16));

        Collections.sort(list);

        for (Student student : list) {
            System.out.println(student);
        }


    }
}

5.扩展

        如果在使用的时候,想要独立的定义规则去使用可以采用Collections.sort(List list,Comparetor<T> c)方式,自己定义规则:

Collections.sort(list, new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        return o2.getAge()-o1.getAge();//以学生的年龄降序
    }
});

效果:

Student{name='rose', age=18}
Student{name='ace', age=17}
Student{name='jack', age=16}
Student{name='abc', age=16}
Student{name='mark', age=16} 

 如果想要规则更多一些,可以参考下面的代码:

Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                // 年龄降序
                int result = o2.getAge()-o1.getAge();//年龄降序

                if(result==0){//第一个规则判断完了 下一个规则 姓名的首字母 升序
                    result = o1.getName().charAt(0)-o2.getName().charAt(0);
                }
                return result;
            }
        });

效果如下:

Student{name='rose', age=18}
Student{name='ace', age=17}
Student{name='abc', age=16}
Student{name='jack', age=16}
Student{name='mark', age=16} 

七、Map集合

 1.概述

        现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即java.util.Map接口。我们通过查看Map接口描述,发现Map接口下的集合与Collection接口下的集合,它们存储数据的形式不同,如下图所示:

a.Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。

b.Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。

c.Collection中的集合称为单列集合,Map中的集合称为双列集合。

d.Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。

2.Map常用子类

        通过查看Map接口描述,看到Map有多个子类,此处主要介绍常用的HashMap集合、LinkedHashMap集合。

a.HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

b.LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

tips:Map接口中的集合都有两个泛型变量<K,V>,在使用时,要为两个泛型变量赋予数据类型。两个泛型变量<K,V>的数据类型可以相同,也可以不同。

3.Map接口中的常用方法

Map接口中定义了很多方法,常用的如下:

a.public V put(K key, V value):把指定的键与指定的值添加到Map集合中。

b.public V remove(Object key):把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。

c.public V get(Object key):根据指定的键,在Map集合中获取对应的值。

d.boolean containsKey(Object key):判断集合中是否包含指定的键。

e.public Set<K> keySet():获取Map集合中所有的键,存储到Set集合中。

f.public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。

import java.util.HashMap;

public class Test {
    public static void main(String[] args) {
        //创建 map对象
        HashMap<String, String> map = new HashMap<String, String>();

        //添加元素到集合
        map.put("1", "张三");
        map.put("2", "李四");
        map.put("3", "王五");
        System.out.println(map);

        //String remove(String key)
        System.out.println(map.remove("3"));
        System.out.println(map);

        // 想要查看2的对应的是谁
        System.out.println(map.get("2"));
        System.out.println(map.get("3"));
    }
}

tips:使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回null,并把指定的键值添加到集合中;若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。

4.Map集合遍历键找值方式

 键找值方式:即通过元素中的键,获取键所对应的值。

操作步骤:

a.获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。

b.遍历键的Set集合,得到每一个键。

c.根据键,获取键所对应的值。

代码演示:

import java.util.HashMap;
import java.util.Set;

public class Test {
    public static void main(String[] args) {
        //创建Map集合对象
        HashMap<String, String> map = new HashMap<String,String>();
        //添加元素到集合
        map.put("胡歌", "霍建华");
        map.put("郭德纲", "于谦");
        map.put("薛之谦", "大张伟");

        //获取所有的键  获取键集
        Set<String> keys = map.keySet();
        // 遍历键集 得到 每一个键
        for (String key : keys) {
            //key  就是键
            //获取对应值
            String value = map.get(key);
            System.out.println(key+"的CP是:"+value);
        }
    }
}

遍历图解:

 5.Entry键值对对象

        我们已经知道,Map中存放的是两种对象,一种称为key(键),一种称为value(值),它们在在Map中是一一对应关系,这一对对象又称做Map中的一个Entry(项)Entry将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值。既然Entry表示了一对键和值,那么也同样提供了获取对应键和对应值得方法:

a.public K getKey():获取Entry对象中的键。

b.public V getValue():获取Entry对象中的值。

在Map集合中也提供了获取所有Entry对象的方法:

a.public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。

6.Map集合遍历键值对方式

键值对方式:即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值。

操作步骤:

a.获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。

b.遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象。

c.通过键值对(Entry)对象,获取Entry对象中的键与值。 

代码演示:

import java.util.*;

public class Test {
    public static void main(String[] args) {
        // 创建Map集合对象
        HashMap<String, String> map = new HashMap<String,String>();
        // 添加元素到集合
        map.put("胡歌", "霍建华");
        map.put("郭德纲", "于谦");
        map.put("薛之谦", "大张伟");

        // 获取 所有的 entry对象  entrySet
        Set<Map.Entry<String,String>> entrySet = map.entrySet();

        // 遍历得到每一个entry对象
        for (Map.Entry<String, String> entry : entrySet) {
            // 解析
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key+"的CP是:"+value);
        }
    }
}

遍历图解:

tips:Map集合不能直接使用迭代器或者foreach进行遍历,但是转成Set之后就可以使用了。 

7.HashMap存储自定义类型键值

         每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到map集合中。学生作为键, 家庭住址作为值。

   编写学生类:

public class Student {
    private String name;
    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

编写测试类:

import java.util.*;

public class Test {
    public static void main(String[] args) {
        //1,创建Hashmap集合对象。
        Map<Student,String>map = new HashMap<Student,String>();
        //2,添加元素。
        map.put(new Student("张三",28), "上海");
        map.put(new Student("李四",22), "北京");
        map.put(new Student("王五",24), "成都");
        map.put(new Student("赵六",25), "广州");
        map.put(new Student("张三",22), "南京");

        //3,取出元素。键找值方式
        Set<Student>keySet = map.keySet();
        for(Student key: keySet){
            String value = map.get(key);
            System.out.println(key.toString()+"....."+value);
        }
    }
}

a.当给HashMap中存放自定义对象时,如果自定义对象作为key存在,这时要保证对象唯一,必须复写对象的hashCode和equals方法。

b.如果要保证map中存放的key和取出的顺序一致,可以使用java.util.LinkedHashMap集合来存放。

8.LinkedHashMap

        我们知道HashMap保证成对元素唯一,并且查询速度很快,可是成对元素存放进去是没有顺序的,那么我们要保证有序,就可以使用在HashMap下面的一个子类LinkedHashMap,它是链表和哈希表组合的一个数据存储结构。

import java.util.*;

public class Test {
    public static void main(String[] args) {
        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
        map.put("1", "张三");
        map.put("2", "李四");
        map.put("3", "王五");
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        for (Map.Entry<String, String> entry : entrySet) {
            System.out.println(entry.getKey() + "  " + entry.getValue());
        }
    }
}

9.HashTable

        HashTable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢,Hashtable集合,不能存储null值和null键,HashTable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了,Hashtable的子类Properities依然活跃在历史舞台,Properties是一个唯一和IO流结合的集合。

import java.util.*;

public class Test {
    public static void main(String[] args) {
        HashMap<String,String>  map  = new HashMap<>();
        map.put(null,"a");
        map.put("b",null);
        map.put(null,null);
        System.out.println(map);

        Hashtable<String,String> table = new Hashtable<>();
//        table.put(null,"a");//.NullPointerException
//        table.put("a",null);//.NullPointerException
        table.put("a","b");
        System.out.println(table);
    }
}

10.Map集合练习

 需求: 计算一个字符串中每个字符出现次数。        

分析:

a.获取一个字符串对象。

b.创建一个Map集合,键代表字符,值代表次数。

c.遍历字符串得到每个字符。

d.判断Map中是否有该键。

e.如果没有,第一次出现,存储次数为1;如果有,则说明已经出现过,获取到对应的值进行++,再次存储。

f.打印最终结果。

代码:

import java.util.*;

public class Test {
    public static void main(String[] args) {
        //友情提示
        System.out.println("请输入一个字符串:");
        String line = new Scanner(System.in).nextLine();
        // 定义 每个字符出现次数的方法
        findChar(line);
    }
    private static void findChar(String line) {
        //1:创建一个集合 存储  字符 以及其出现的次数
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        //2:遍历字符串
        for (int i = 0; i < line.length(); i++) {
            char c = line.charAt(i);
            //判断 该字符 是否在键集中
            if (!map.containsKey(c)) {//说明这个字符没有出现过
                //那就是第一次
                map.put(c, 1);
            } else {
                //先获取之前的次数
                Integer count = map.get(c);
                //count++;
                //再次存入  更新
                map.put(c, ++count);
            }
        }
        System.out.println(map);
    }
}

八、模拟斗地主洗牌发牌

 1.介绍

按照斗地主的规则,完成洗牌发牌的动作。

具体规则:

a.组装54张扑克牌将。

b.54张牌顺序打乱。

c.三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。

d.查看三人各自手中的牌(按照牌的大小排序)、底牌。

2. 需求分析

        a.准备牌:

        完成数字与纸牌的映射关系:使用双列Map(HashMap)集合,完成一个数字与字符串纸牌的对应关系(相当于一个字典)。

        b.洗牌:

        通过数字完成洗牌发牌。

        c.发牌:

        将每个人以及底牌设计为ArrayList<String>,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。存放的过程中要求数字大小与斗地主规则的大小对应。将代表不同纸牌的数字分配给不同的玩家与底牌。

        d.看牌:

        通过Map集合找到对应字符展示。通过查询纸牌与数字的对应关系,由数字转成纸牌字符串再进行展示。

3.实现代码

public class Test {
    public static void main(String[] args) {

        //创建Map集合存储
        HashMap<Integer, String> pokerMap = new HashMap<Integer, String>();
        //创建花色集合与数字集合
        ArrayList<String> colors = new ArrayList<String>();
        ArrayList<String> numbers = new ArrayList<String>();
        //存储花色与数字
        Collections.addAll(colors, "♦", "♣", "♥", "♠");
        Collections.addAll(numbers, "2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");
        //设置存储编号变量
        int count = 1;
        pokerMap.put(count++, "大王");
        pokerMap.put(count++, "小王");
        //创建牌存储到map集合中
        for (String number : numbers) {
            for (String color : colors) {
                String card = color + number;
                pokerMap.put(count++, card);
            }
        }
        
        //取出编号集合
        Set<Integer> numberSet = pokerMap.keySet();
        //转换到list集合中
        ArrayList<Integer> numberList = new ArrayList<Integer>();
        numberList.addAll(numberSet);

        // 打乱顺序
        Collections.shuffle(numberList);
   
        //创建三个玩家编号集合和一个底牌编号集合
        ArrayList<Integer> noP1 = new ArrayList<Integer>();
        ArrayList<Integer> noP2 = new ArrayList<Integer>();
        ArrayList<Integer> noP3 = new ArrayList<Integer>();
        ArrayList<Integer> dipaiNo = new ArrayList<Integer>();

        //发牌的编号
        for (int i = 0; i < numberList.size(); i++) {
            // 获取该编号
            Integer no = numberList.get(i);
            // 发牌
            if (i >= 51) {
                dipaiNo.add(no);
            } else {
                if (i % 3 == 0) {
                    noP1.add(no);
                } else if (i % 3 == 1) {
                    noP2.add(no);
                } else {
                    noP3.add(no);
                }
            }
        }

        //查看三人各自手中的牌(按照牌的大小排序)、底牌
        //对手中编号进行排序
        Collections.sort(noP1);
        Collections.sort(noP2);
        Collections.sort(noP3);
        Collections.sort(dipaiNo);

        //进行牌面的转换
        //创建三个玩家牌面集合 以及底牌牌面集合
        ArrayList<String> player1 = new ArrayList<String>();
        ArrayList<String> player2 = new ArrayList<String>();
        ArrayList<String> player3 = new ArrayList<String>();
        ArrayList<String> dipai = new ArrayList<String>();

        //转换
        for (Integer i : noP1) {
            //根据编号找到 牌面 pokerMap
            String card = pokerMap.get(i);
            //添加到对应的牌面集合中
            player1.add(card);
        }

        for (Integer i : noP2) {
            String card = pokerMap.get(i);
            player2.add(card);
        }
        for (Integer i : noP3) {
            String card = pokerMap.get(i);
            player3.add(card);
        }
        for (Integer i : dipaiNo) {
            String card = pokerMap.get(i);
            dipai.add(card);
        }

        //查看
        System.out.println("张三:"+player1);
        System.out.println("李四:"+player2);
        System.out.println("王五:"+player3);
        System.out.println("底牌:"+dipai);
    }
}

     

  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值