集合的学习
集合引入:在学习的过程中,学习了数组这个容器,数组的存储数据给我们提供了方便,但是一个简单的数组容器无法满足我们的需求,所以jdk又在数组的基础上诞生了集合,而且在不断的发展中,集合现在已经是一个大家族了
数组与集合关系:
- 数组是容器,同样集合也是容器
- 数组可以指定类型,同样集合也可以指定类型
- 数组长度是固定的不能改变,集合的长度是不定的可以改变
可以看出,集合的区别在于它可以改变自身的长度。可以改变自己的长度,所以jdk根据集合制定了很多的类,很多的方法提供我们使用,这里学习了些许常用的。
常见的三大集合接口:List接口,set接口,Map接口, 由于接口无法直接创建对象使用,所以我们绝大多数都会去使用它们的实现类。
集合简介:
![](https://img2020.cnblogs.com/blog/1845592/202008/1845592-20200813102334353-724338924.png)
List接口:(单列有序集合)
这里的无序和有序指的是添加顺序并非像(1~10)这样的自然顺序
list接口所有已知实现类如下:经常使用的是ArrayList和LinkedList。所以关于list接口我也只学习了这两个。
ArrayList实现类
ArrayList的定义(如图所示:ArrayList有三个构造方法):
public static void main(String[] args) { /* 第一种: 构造一个初始量为默认的集合 */ ArrayList<String> arrayList = new ArrayList<String>(); /* 第一种: 构造一个添加指定集合的ArrayList集合 */ ArrayList<String> arrayList2 = new ArrayList<String>(arrayList) ; /* 第三种: 构造一个初始量为20的集合 */ ArrayList<String> arrayList3 = new ArrayList<String>(20); }
ArrayList使用方法:
- 导包:import java.util.ArrayList;
- 创建对象:ArrayList<String> arrayList = new ArrayList<String>();
在尖括号里面的有一个 E 取自Element(元素)的首字母。代表任意元素的意思,List集合不能存储基本数据类型 例如:int 但可以存储它的包装类 Integer,
ArrayList常用方法:
- add():为集合添加元素,在集合末尾追加;add(index,E element):在指定索引出添加元素。
- clear():清除集合所有元素。
contains(Object o):判断集合是否包含该元素,包含返回true,不包含返回false;
isEmpty():判断元素是否不包含,与contains相反
forEach(Consumer<? super E> action):对集合遍历操作。
indexOf(Object o):返回元素索引。
iterator():迭代器遍历。
remove():删除指定索引位置的元素;
remove(Object o):删除指定元素,如果该元素有多个,则删除第一次出现的那个。
set(int index, E element):修改指定索引处的元素。
size():返回集合列表长度。
代码演示:
public class ArrayListTest { public static void main(String[] args) { ArrayList<Integer> arryList = new ArrayList<Integer>(); // 创建一个默认容量为10的集合,类型为Integer // 添加方法使用 arryList.add(12); arryList.add(-2); // 直接追加式; arryList.add(0, 67); // 索引插入式 // 遍历集合方法 arryList.forEach((s) -> System.out.print(s + " ")); // forEach遍历加lamdba表达式 // 是否包含方法 System.out.println(arryList.contains(21)); // 集合是否包含21,返回false // set修改方法 arryList.set(0, 7); // 修改索引为0的元素:把67修改为7 // iterator 迭代器遍历 Iterator<Integer> it = arryList.iterator(); System.out.print("迭代器遍历:"); while(it.hasNext()) { System.out.print(it.next()+" "); } //返回元素12的下标索引 System.out.println("\n12的索引:"+arryList.indexOf(12)); //remove元素删除方法 arryList.remove(0); //根据下标删除 返回布尔类型 arryList.remove("7"); //根据内容删除元素,删除元素为7的元素。返回布尔型 //clear()清除所有元素方法 arryList.clear(); //isEmpty 判空方法 boolean empty = arryList.isEmpty(); if (empty) { System.out.println("该集合为空"); } } }
ArrayList存储类型:
![](https://img2020.cnblogs.com/blog/1845592/202008/1845592-20200813115242790-1537909427.png)
ArrayList对象类型操作:
public class ArrayListTest02 { public static void main(String[] args) { // 创建存储学生类的集合 ArrayList<Student> studentList = new ArrayList<Student>(); //添加数据 studentList.add(new Student("张三", 21)); studentList.add(new Student("李四", 12)); studentList.add(new Student("王五", 34)); //打印:Student [name=张三, age=21]Student [name=李四, age=12]Student [name=王五, age=34] studentList.forEach((s) -> System.out.print(s)); //同样一系列的增删查改操作都可以在这里使用 } } //实体类 Student class Student { private String name; private int age; public Student(String name, int age) { super(); this.name = name; this.age = age; } public Student() { } 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 + "]"; } }
LinkedList实现类
LinkedList和ArrayList一样属于单列集合,在集合中很多方法都是由父类接口提供,所以大不分的方法都是通用的如:在ArrayList的add方法同样适用于LinkedList集合。但不同的实现类又会有几个特有的方法。
LinkedList方法:
addFirst(E e):从集合开头插入元素。
addLast(E e):将指定元素添加到集合尾部。
get(int index):根据索引返回元素;
getFirst():返回列表第一个元素;
getLast():返回列表最后一个元素。
offer(E e):将指定元素添加为集合的最后的一个元素;
offerFirst(E e)
:在此集合头部添加指定元素;
offerLast(E e):在集合尾部添加元素。
removeFirst():删除列表第一个元素,并返回该元素;
removeLast():删除列表最后一个元素,并返回该元素。
代码演示:
public class LinkedListTest { public static void main(String[] args) { //创建对象 LinkedList<Integer> linkedList = new LinkedList<Integer>(); linkedList.add(12); linkedList.add(21); linkedList.add(3); linkedList.add(32); System.out.println("初始化列表"); linkedList.forEach(System.out::println); System.out.println("获取元素"); System.out.println(linkedList.get(1));//获取索引为0处是元素 // 打印21 System.out.println(linkedList.getFirst());//获取列表第一个元数 //打印12 System.out.println(linkedList.getLast());//获取列表最后一个元素//打印32 //删除 Integer removeFirst = linkedList.removeFirst(); //删除列表第一个元数,并返回该元素 Integer removeLast = linkedList.removeLast(); //删除列表最后一个元素,并返回该元素 System.out.println("删除后还剩余"); linkedList.forEach(System.out::println); } }
LinkedList和ArrayList的区别:
- 结构不同:LinkedList基于链表的数据结构,ArrayList是基于数组的的数据结构
有于底层的结构不同,就导致了ArrayList 元素 增删慢,查找快 的特点 ,LinkedList 元素 增删快,查找慢 的特点,所以根据自己的需求,在和里的场景使用两者。两者处理数据量不大时,其实没啥区别。
set接口(单列无序集合)
set和list的区别:最主要的区别就是有序和无序,两个集合在添加数据时,list集合按照添加顺序依次添加,而set集合则会打乱添加顺序。演示如下:
public class HashSetTest { public static void main(String[] args) { ArrayList<Number> arrList = new ArrayList<Number>(); arrList.add(new Number(1)); arrList.add(new Number(2)); arrList.add(new Number(3)); arrList.add(new Number(4)); System.out.println("list集合打印:"); arrList.forEach((s) -> System.out.println(s + " ")); /* * list集合打印: Number [i=1] Number [i=2] Number [i=3] Number [i=4] */ HashSet<Number> hashSet = new HashSet<Number>(); hashSet.add(new Number(1)); hashSet.add(new Number(2)); hashSet.add(new Number(3)); hashSet.add(new Number(4)); System.out.println("set集合打印:"); hashSet.forEach((s) -> System.out.println(s + " ")); /* * set集合打印: Number [i=3] Number [i=4] Number [i=1] Number [i=2] */ } } class Number { int i; public Number(int i) { this.i = i; } @Override public String toString() { return "Number [i=" + i + "]"; } }
Set接口两个常用实现类:HashSet实现类 ,LinkedHashSet实现类
HashSet实现类
hashSet集合底层存储结构:
![](https://img2020.cnblogs.com/blog/1845592/202008/1845592-20200813114607691-100882962.png)
public class HashSetTest { public static void main(String[] args) { HashSet<Number> hashSet = new HashSet<Number>(); hashSet.add(new Number(1)); hashSet.add(new Number(2)); hashSet.add(new Number(3)); hashSet.add(new Number(4)); System.out.println("set集合打印:"); hashSet.forEach((s) -> System.out.println(s + " ")); /* 重写前: * set集合打印: Number [i=3] Number [i=4] Number [i=1] Number [i=2] */ /*重写后: * set集合打印: Number [i=1] Number [i=2] Number [i=3] Number [i=4] */ } } class Number { int i; public Number(int i) { this.i = i; } @Override public String toString() { return "Number [i=" + i + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + i; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Number other = (Number) obj; if (i != other.i) return false; return true; } }
equals判断两个对象内容是否相等,hashCode判断两个对象地址值是否相等,这也间接的是set集合不会出现重复值,除去自己定义的引用类外,jdk里面的引用数据类型,也默认重写了这两个方法。(这也是set集合和list集合的一大区别):
public static void main(String[] args) { ArrayList<Integer> arrayList = new ArrayList<Integer>(); arrayList.add(12); arrayList.add(2); arrayList.add(12); arrayList.forEach(System.out::println);//打印了12,2,12允许重复值出现 System.out.println("-------------------------"); HashSet<Integer> hashSet = new HashSet<Integer>(); hashSet.add(12); hashSet.add(2); hashSet.add(12); hashSet.forEach(System.out::println);//打印了2 12,默认去重 }
LinkedHashSet实现类
LinkedHashSet概述:
LinkedHashSet 有着Linked的特性,也有这set的特性,底层也有着hash的特点,可以这样说,LinkedHashSet 是一个有序且不能重复的集合,它是链表和哈希表组合的一个数据存储结构。 所以除了重写equals外还可以选择LinkedHashSet 来存储一个有序且不重复的集合。
public static void main(String[] args) { LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<Integer>(); linkedHashSet.add(21); linkedHashSet.add(2); linkedHashSet.add(28); linkedHashSet.add(11); linkedHashSet.add(10); linkedHashSet.forEach(System.out::println);//依序输出 }
至此,单列集合结束
Map(双列集合)
map引入:现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即 java.util.Map 接口。
既然是映射,所以map需要两个引用类型,结构:Interface Map<K,V> map是接口,k代表 key(键值),v代表 value(值)。
map常用实现类:
-
HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
-
LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
HashMap<K,V>实现类
hashMap常用方法:
- public V put(K key, V value) : 把指定的键与指定的值添加到Map集合中。
- public V remove(Object key) : 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
- public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
- boolean containsKey(Object key) 判断集合中是否包含指定的键。
- public Set<K> keySet() : 获取Map集合中所有的键,存储到Set集合中。
- public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。
- public Set<K> keySet():获取map中的所有key值存储到set集合并返回。
hashMap示列代码:
public class HashMapTest { public static void main(String[] args) { HashMap<Integer, String> hashMap = new HashMap<Integer, String>(); //定义一个key为Integr类型,values为String类型的HashMap //添加方法 hashMap.put(1, "张三"); hashMap.put(2, "李四"); hashMap.put(3, "王五"); hashMap.forEach((k,v) ->System.out.println(k+"="+v)); //keySet方法 Set<Integer> keySet = hashMap.keySet();//返回包含集合中所有key值的集合 keySet.forEach(System.out::print); } }
集合的操作都大同小异,就不写太多了,显得啰嗦多余。
hashMap的特点:
- 存在 k v两个参数值
- key值不能重复,如有两个相同,后者会覆盖前者的值
- HashMap由数组+链表组成的
- 存储的值不是有序的
- 在使用自定义的对象当参数时,需要重写equals和hashCode方法
LinkedHashMap
看区别,LinkedHashMap和HashMap无非就是有序和无序的区别
public class LinkedHashMapTest { public static void main(String[] args) { HashMap<Integer, String> hashMap = new HashMap<Integer, String>(); hashMap.put(52, "张三"); hashMap.put(9, "李四"); hashMap.put(21, "王五"); hashMap.forEach((k,v) ->{System.out.println(k+"="+v);}); /* 随机存储 * 52=张三 21=王五 9=李四 */ LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<Integer, String>(); linkedHashMap.put(52, "张三"); linkedHashMap.put(9, "李四"); linkedHashMap.put(21, "王五"); linkedHashMap.forEach((k,v) ->{System.out.println(k+"="+v);}); /*有序存储 * 52=张三 9=李四 21=王五 */ } }
不做过多的介绍,一个写通了就都玩明白了。
集合综合案例:
- 按照斗地主的规则,完成洗牌发牌的动作。
- 组装54张扑克牌将
- 54张牌顺序打乱
- 三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
- 查看三人各自手中的牌(按照牌的大小排序)、底牌
代码如下:
public class Poker { public static void main(String[] args) { /** 1组装54张扑克牌 */ // 1.1 创建Map集合存储 Map<Integer, String> pokerMap = new HashMap<Integer, String>(); // 1.2 创建 花色集合 与 数字集合 List<String> colors = new ArrayList<String>(); List<String> numbers = new ArrayList<String>(); // 1.3 存储 花色 与数字 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++, "小王"); // 1.4 创建牌 存储到map集合中 for (String number : numbers) { for (String color : colors) { String card = color + number; pokerMap.put(count++, card);/** 2 将54张牌顺序打乱 */ // 取出编号 集合 } } Set<Integer> numberSet = pokerMap.keySet(); // 因为要将编号打乱顺序 所以 应该先进行转换到 list集合中 List<Integer> numberList = new ArrayList<Integer>(); numberList.addAll(numberSet); // 打乱顺序 Collections.shuffle(numberList); // 3 完成三个玩家交替摸牌,每人17张牌,最后三张留作底牌 // 3.1 发牌的编号 // 创建三个玩家编号集合 和一个 底牌编号集合 List<Integer> noP1 = new ArrayList<Integer>(); List<Integer> noP2 = new ArrayList<Integer>(); List<Integer> noP3 = new ArrayList<Integer>(); List<Integer> dipaiNo = new ArrayList<Integer>(); // 3.2发牌的编号 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); } } }// 4 查看三人各自手中的牌(按照牌的大小排序)、底牌 // 4.1 对手中编号进行排序 Collections.sort(noP1); Collections.sort(noP2); Collections.sort(noP3); Collections.sort(dipaiNo); // 4.2 进行牌面的转换 // 创建三个玩家牌面集合 以及底牌牌面集合 List<String> player1 = new ArrayList<String>(); List<String> player2 = new ArrayList<String>(); List<String> player3 = new ArrayList<String>(); List<String> dipai = new ArrayList<String>(); // 4.3转换 for (Integer i : noP1) { // 4.4 根据编号找到 牌面 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); } //4.5 查看 System.out.println("令狐冲:"+player1); System.out.println("石破天:"+player2); System.out.println("鸠摩智:"+player3); System.out.println("底牌:"+dipai); } }
个人学习,内容简略。