第八章 集合

集合框架三要素: 接口、实现类、数据结构

单列集合根接口:java.util.Collection

多列集合根 接口:java.util.Map

1.Vector集合、ArrayList集合、LinkedList集合的区别和联系。

Vector是基于数组的,线程安全。在容量不够的情况下,内存增长为原来的一倍;

ArrayList基于数组但是线程不安全,增长为原数组的50%。增删慢,查找快。

LinkedList是基于双向链表线程不安全的。增删快,查找慢

2.List和Set集合对比

List和Set都是继承Collection接口的。

区别:List是⼀种有序、带索引、可以存放重复数据的集合

Set是⼀种⽆序、不带下标索引、不能存放重复数据的集合


一、Collection使用方法
1.存放指定类型
 boolean  contains(Object o);//判断集合是否包含给定元素,借助equals方法。不是使用地址判断的
		Collection c1 = new ArrayList<>();
		String s1 = "hello";
		String s2 = "world";
		String s3 = new String("world");
		c1.add(s1);
		boolean f1 = c1.contains("hello");//true
		f1 = c1.contains(s3);//true,s3是堆中临时new出来的和s1(存在字符串常量池中)地址不一样,但指向的内容一样,
Object[] toArray() //把集合存储到数组中

boolean containsAll(Collection c);//判断当前集合中是否包含给定的集合的所有元素。

boolean retainAll(Collection c)判断俩个集合中是否有相同的元素,如果有当前集合只保留相同元素,如果没有当前集合元素清空 
2、存放自定义类对象

要使用contains和remove方法是必须得重写equals方法

public static void main(String[] args) {
        //创建学生对象
        Student s1 = new Student("zs",12,"nan");
        Student s2 = new Student("as",25,"nv");
        Student s3 = new Student("zs" ,12,"nan");
        Collection coll = new ArrayList<>();
        coll.add(s1);
        coll.add(s2);
        System.out.println(coll.size());
        System.out.println(coll);//s1和s3属性值相同,应该是同一个人
        //判断s3是否存在
        boolean f = coll.contains(s3);
        System.out.println(f);//false 按理来说是存在的

        //删除s3
        boolean flag = coll.remove(s3);
        System.out.println(flag);
        //出现这种情况要重写equals来比较,重写之后f和flag都为true
    }
class Student{
     @Override
    public boolean equals(Object obj){
        if(this == obj)//this输入的值,obj原来的值
            return true;
        if(this == null)
            return false;
        if(getClass() != obj.getClass())
            return false;
        Student other = (Student) obj;
        if(age != other.age)
            return false;
        if(name == null){
            if(other.name != null)
                return false;
        } else if (! name.equals(other.name)) {
            return false;
        }
        return true;
    }
}
3、集合遍历
  • toArray();

//将集合转化成数组
Object[] array = 集合引用.toArray();
//遍历数组
for (int i = 0; i < array.length; i++) {
			System.out.println(array[i]);
}
  • 迭代器

1.获取迭代器对象
Iterator<集合元素类型>  iterator = 集合对象.iterator();
//借助hasNext()和next()完成遍历
while(iteratro.hashNext()){
	//获取集合元素
	集合元素类型 变量名 = iterator.next();
	//对集合元素进行输出
	System.out.println(变量名);
}
  • foreach(Collection和其子类集合,数组,都可以用foreach循环遍历)

for(集合元素类型 变量名 : 集合) {
//操作元素变量
}
二、List接口
1、常用方法
get(int index);//返回集合中指定位置的元素
set(int index, E element);//用指定元素替换集合中指定位置的元素,并返回被替代的旧元素。
add(int index, E element);//将指定的元素,添加到该集合中的指定位置上。
boolean addAll(int index, Collection c);//从指定位置开始,把另一个集合的所有元素添加进来
remove(int index);//移除列表中指定位置的元素, 并返回被移除的元素。
int indexOf(Object o);//查收指定元素在集合中的所有,从前往后查到的第一个元素(List集合可以重复存放数据)
int lastIndexOf(Object o);
List subList(int fromIndex, int toIndex);//根据指定开始和结束位置,截取出集合中的一部分数据
2、遍历方法
  • 使用get方法

//遍历集合
   for(int i = 0;i
  • 使用foreach

for(集合元素类型 变量名 : 集合) {
//操作元素变量
}
  • 迭代器遍历

Iterator<集合元素类型> iterator = 集合对象名.iterator();
while(iterator.hasNext()){
	集合类型元素 变量名 = iterator.next();
	system.iut.println(变量名);
}
3、LinkedList中头尾节点操作方法
void addFirst(E e)//将指定元素插入此列表的开头
void addLast(E e)//将指定元素添加到此列表的结尾
E getFirst()//返回此列表的第一个元素
E getLast()//返回此列表的最后一个元素
E removeFirst()//移除并返回此列表的第一个元素
E removeLast()//移除并返回此列表的最后一个元素
E pop()//从此列表所表示的堆栈处弹出一个元素
void push(E e)//将元素推入此列表所表示的堆栈
三、Set集合
1、遍历方法

foreach、迭代器

2、HashSet(基于Hash Table)

无序、唯一、允许null元素、高效性

HashSet存放自定义类对象时元素插入过程:

  • 当向HashSet中插入元素时,先获取元素的hashCode()值,

    再检查HashSet中是否存在相同哈希值的元素,

    如果元素哈希值唯一,则直接插入元素

  • 如果存在相同哈希值的元素,会调用元素的equals()方法来进一步判断元素是否是相同。如果相同,则不会插入重复元素;如果不同,则插入

所以为了避免出现属性值相同对象的重复插入,需要类中重写equals()方法和hashCode()方法。

3、为什么要同时重写equals方法和hashcode方法

这是因为在 Java 中,对象的相等性判断是基于 equals() 方法的,而 hashCode() 方法在集合类中常用于快速查找和存储对象。在使用集合类(如 HashMap、HashSet 等)时,对象作为键或值存储在集合中,它们的相等性判断和查找都依赖于 equals() 和 hashCode() 方法。

所以,为了保证对象的正确比较和查找,在重写 equals() 方法时,必须同时重写 hashCode() 方法。如果两个对象是相等的(即 equals() 方法返回 true),那么它们的 hashCode() 方法应该返回相同的哈希码。

如果你只重写了 equals() 方法而没有重写 hashCode() 方法,那么在集合类中进行查找或比较时可能会出现意外的结果,因为对象的哈希码可能不匹配。因此,为了保持对象在比较和查找时的一致性和正确性,通常我们需要同时重写 equals() 方法和 hashCode() 方法。确保在 equals() 方法返回 true 时,hashCode() 方法返回相同的哈希码。这样就可以确保对象在集合类中的正确性和一致性操作。

4、TreeSet(基于红黑树)

有序性(能够自动调整顺序)、唯一性、动态性、高效性

存放自定义对象时由于类对象是不可比较的,会出现ClassCastException异常

因为:TreeSet是一个有序集合,存储数据时,一定要指定元素的排序规则,有两种指定的方式

  • 自然排序(元素所属类型要实现java.lang.Comparable 接口)

public class Test073_Person {
    public static void main(String[] args) {
        Set set = new TreeSet<>();
        set.add(new Person("zs",22));
        set.add(new Person("li",25));
        set.add(new Person("ww",12));
        Iterator it = set.iterator();
        while(it.hasNext()){
            Person p  =it.next();
            System.out.println(p);
        }
    }
}
class Person implements Comparable{
    private String name;
    private int age;
    public Person(){};
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
    @Override
    public int compareTo(Person o){
        //先按name升序,name相同则按age降序
		//this.name已有元素,o.name这次要插入的元素
        if(o.name.equals(this.name)){
            if(o.age != this.getAge()){
                return this.getAge-o.age;
            }
        }
        return this.name.compareTo(o.name);
    }
}

compareTo方法说明:(比较字符串)

int result = this.属性.compareTo(o.属性);

result的值大于0,说明this比o大 (升序)

result的值小于0,说明this比o小 (降序)

result的值等于0,说明this和o相等

  • 比较器排序

创建Comparator接口。重写compare()方法

创建TreeSet对象,将比较器对象作为参数传递给构造函数

public class Test073_Comparator {
    public static void main(String[] args) {
        //适用匿名内部类
        Comparator comp = new Comparator(){
            //重写比较算法:先按按age升序,age相同则按name降序
            @Override
            public int compare(Person1 o1,Person1 o2){
                if(o1.getAge() != o2.getAge()){
                    return o1.getAge()-o2.getAge();
                }else {
                    return o2.getName().compareTo(o1.getName());
                }
            }
        };
        Set set = new TreeSet<>(comp);
        //3.添加元素
        set.add(new Person1("zs",21));
        set.add(new Person1("ww",20));
        set.add(new Person1("zs",21));
        set.add(new Person1("tom",19));
        set.add(new Person1("tom",23));
        set.add(new Person1("jack",20));

        for(Person1 p: set){
            System.out.println(p);
        }
    }
}
5、LinkedHashSet

LinkedHashSet 是HashSet 的一个子类,具有HashSet 的高效性能和唯一性特性,并且保持了元素的插入顺序,其底层基于哈希表和链表实现。

四、Map集合(哈希表和红黑树)

最长用HashMap()

存储元素时,必须以key-value(键值对)的方式进行、键唯一、可重复值、高效查询和访问、

1、常用方法
put(K key, V value)把key-value存到当前Map集合中,key存在则会覆盖原来的value值
putAll();把指定map中的所有key-value,存到当前Map集合中
boolean containsKey(Object key)//当前Map集合中是否包含指定的key值
boolean containsValue(Object value)//当前Map集合中是否包含指定的value值
Set keySet()//返回Map集合中所有的key值
Collection values()//返回Map集合中所有的value值
Set> entrySet()//把Map集合中的的key-value封装成Entry类型对象,再存放到set集合中,并返回
V get(Object key)   //在当前Map集合中,通过指定的key值,获取对应的value
2、Map遍历
  • 借助Map中的keySet方法,获取一个Set集合对象,而遍历Set集合获取每一个key值,再根据key获取对应的value。

    //1,获取key,根据key获取value
            Set set = map.keySet();//Integer为键的类型
            for(Integer k : set){
                String v = map.get(k);
                System.out.println("id:" + k+ "," + "name:" + v);
            }

  • 助Map中的entrySet方法,获取一个Set对象,内部包含了Map集合中所有的键值对,然后对键值对进行拆分,得到key和value进行输出。

    //获取所有的key-value键值对,得到一个Set集合
            Set> entrySet = map.entrySet();
            //遍历set集合
            for(Map.Entry entry : entrySet){
                //拆分键值对中的key和value
                Integer key  = entry.getKey();
                String value = entry.getValue();
                System.out.println("id: " + key + " name: " + value);
            }

3、HashMap

HashMap底层借助哈希表实现,元素的存取顺序不能保证一致。

HashMap存储的键值对时,如果键类型为自定义类,那么一般需要重写键所属类的hashCode()和equals()方法(重要,最常用)。

特点:键唯一、值可重复、无序、线程不安全、键和值允许使用null

结论:key类型如果为自定义类型,重写其hashCode和equals方法!

  • HashMap中add(key,value)时,需要判断key是否存在(先hashCode再equals)

  • HashMap中containsKey(key)时,同样要借助hashCode和equals方法

  • HashMap中remove(key)时,同样要借助hashCode和equals方法

4、Hashtable

线程安全、键和值都不能为空、哈希表实现

5、TreeMap

键是按照自然顺序或自定义比较器进行排序的、红黑树实现、键唯一,值可重复、线程不安全、

会根据插入的键值对动态地调整红黑树的大小。

使用自定义比较器规则排序
public class Test086_Comparator {
    public static void main(String[] args) {
        Comparator comp = new Comparator() {
            @Override
            public int compare(Student2 o1, Student2 o2) {
                //名字降序,姓名一样,则年龄升序
                if(!o1.getName().equals(o2.getName())){
                    return o2.getName().compareTo(o1.getName());
                }else {
                    return o1.getAge()-o2.getAge();
                }
            }
        };
        Map map = new TreeMap<>(comp);
        map.put(new Student2("zs", 78),"010");
        map.put(new Student2("rose", 82),"005");
        map.put(new Student2("lucy", 79),"009");
        map.put(new Student2("lucy", 79),"009");
        map.put(new Student2("tom", 68),"019");
        map.put(new Student2("tom", 86),"012");
        map.put(new Student2("ww", 67),"002");

        Set> entrySet = map.entrySet();
        for(Map.Entry entry : entrySet){
            System.out.println(entry.getKey()+"  :  " + entry.getValue());
        }
    }
}
五、Collections

集合工具类是java.util.Collections ,专门来操作集合对象,里面都是静态方法,可以直接调用。

Collection 是单列集合根接口, Collections 是集合工具类,两者不同!

  • fill方法,使用指定元素替换指定列表中的所有元素

  • max方法,根据元素的自然顺序,返回给定集合的最大元素

  • min方法,根据元素的自然顺序,返回给定集合的最小元素

  • reverse方法,反转集合中的元素

  • sort方法,根据元素的自然顺序,对指定列表按升序进行排序

  • shuffle方法,使用默认随机源对指定列表进行置换,随机打乱

  • addAll方法,往集合中添加一些元素

 六、综合案例

设计实现一个斗地主游戏,该游戏可以生成牌、洗牌、发牌,三名玩家可以看自己手里的牌,最后也能看到三张底牌。

具体要求:

  • 生成54张扑克牌(13个数字*4种花色,再加大小王2张)
  • 洗牌:将54张牌随机打乱
  • 准备三个玩家,三人交替摸牌,最后三张底牌不能摸
  • 查看三人各自手中的牌和底牌,要求按牌的大小顺序展示
package com.briup.chap08.test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Test10_Game {
	public static void main(String[] args) {
		// 创建一个Map集合,保存牌面点数和权重值的关系
		Map weights = new HashMap<>	();
		weights.put("3", 1);
		weights.put("4", 2);
		weights.put("5", 3);
		weights.put("6", 4);
		weights.put("7", 5);
		weights.put("8", 6);
		weights.put("9", 7);
		weights.put("10", 8);
		weights.put("J", 9);
		weights.put("Q", 10);
		weights.put("K", 11);
		weights.put("A", 12);
		weights.put("2", 13);
		// 所有可用的牌面点数
		Set numbers = weights.keySet();
		// 所有可用的花色
		Color[] colors = Color.values();
		// 生成52张扑克牌
		List cards = new ArrayList<>();
		for (String number : numbers) {
			for (Color color : colors) {
				cards.add(new Card(number, color, 
						weights.get(number)));
			}
		}
		// 生成两张王牌
		cards.add(new Card("小王", null, 14));
		cards.add(new Card("大王", null, 15));
		// 洗牌
		Collections.shuffle(cards);
		// 表示三个玩家手牌的集合
//		List player1 = new ArrayList<>();
//		List player2 = new ArrayList<>();
//		List player3 = new ArrayList<>();
		
		List> players = Arrays.asList(
				new ArrayList(),
				new ArrayList(),
				new ArrayList()
				);
		// 表示底牌的集合
		List holecards = new ArrayList<>();
		// 获取牌的总数
		int size = cards.size();
		// 底牌数量
		final int holecardsNum = 3;
		// 遍历所有的扑克牌,依次发给每个玩家
		for (int i = 0; i < size; i++) {
			// 取出一张新牌
			Card card = cards.get(i);
			// 最后三张牌,存入底牌集合
			if (i >= size - holecardsNum) {
				holecards.add(card);
				continue;
			}
			int r = i % 3;
			players.get(r).add(card);
		}
		
		Comparator c = new Comparator() {
			@Override
			public int compare(Card o1, Card o2) {
				// 先根据权重(牌面点数)排序
				int w1 = o1.getWeight();
				int w2 = o2.getWeight();
				if (w1 != w2) {
					return w1 - w2;
				} else {
					return o1.getColor().getWeight() 
							- o2.getColor().getWeight();
				}
			}
		};
		// 完成排序
		for (List list: players) {
			list.sort(c);
			System.out.println(list);
		}
		// players.forEach(list -> list.sort(c));
		holecards.sort(c);
		// 输出玩家手牌和底牌
		System.out.println(holecards);
	}
}

enum Color {
	HX("♥", 1), // 红心
	HT("♠", 2), // 黑桃
	FK("♦", 3), // 方块
	CH("♣", 4); // 草花
	private String icon;
	private int weight; 
	private Color(String icon, int weight) {
		this.icon = icon;
		this.weight = weight;
	}
	public int getWeight() {
		return this.weight;
	}
	@Override
	public String toString() {
		return this.icon;
	}
}

// 扑克牌类
class Card {
	
	// 点数
	private final String number; 
	// 花色
	private final Color color;
	// 权重
	private final int weight;
	public Card(String number, Color color, int weight) {
		this.number = number;
		this.color = color;
		this.weight = weight;
	}
	// 获取花色的方法
	public Color getColor() {
		return color;
	}
	// 获取权重的方法
	public int getWeight() {
		return weight;
	}
	
	@Override
	public String toString() {
		return color == null ? number : color + number;
	}
	
}

 

你可能感兴趣的:(Java编程,java,java-ee)