1.集合概述
Java 集合就像一种容器,可以把多个对象的引用放入容器中。
Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组
Java 集合可分为 Set、List 和 Map 三种体系
- Set:无序、不可重复的集合
- List:有序,可重复的集合
- Map:具有映射关系的集合
在 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object 类型处理;从 Java5 增加了泛型以后,Java 集合可以记住容器中对象的数据类型。
2.Collection接口
Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合:
3.List接口
1).List接口简介
List接口为Collection直接接口。List所代表的是有序的Collection,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,
同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。
2).ArrayList集合
ArrayList内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。
当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高(ArrayList在内存不够时默认是扩展50% + 1个)。因此,它适合随机查找和遍历,不适合插入和删除。
public class Test3 {
public static void main(String[] args) {
List<String > list=new ArrayList<>();
list.add("456");//追加到末尾
list.add("hello");
list.add("12311");
list.add("12322");
//1.for
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
//2.foreach
for(String s:list){
System.out.println(s);
}
//3.迭代器
System.out.println("=============");
Iterator<String> it = list.iterator();
/*while (true){
boolean b = it.hasNext();
if(!b){
break;
}
System.out.println(it.next());
}*/
while (it.hasNext()){
System.out.println(it.next());
}
}
}
3).LinkedList集合
LinkedList是用双向链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
4).Vector集合
Vector集合底层数据结构是数组实现的,其操作和ArrayList一致,查询快,增删慢,但是其内部是线程安全,效率低。
5).Itrator接口
Iterator 接口主要用于遍历 Collection 集合中的元素,Iterator 对象也被称为迭代器
Iterator 接口隐藏了各种 Collection 实现类的底层细节,向应用程序提供了遍历 Collection 集合元素的统一编程接口
Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建 Iterator 对象,则必须有一个被迭代的集合。
6).ArrayList和LinkedList的区别如下:
ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
案例1:写一个集合类
需求:编写一个ListCopy类,类具有和ArrayList相似的功能
public class ListCopy {
private int length;
private Object[] obj;
private int index = 0;
public ListCopy(int length) {
this.length = length;
obj = new Object[length];
}
public void add(Object o) {
//如何判断数组是否达到上限
if (index == length) {
//如果上限了,就要扩容
//如何扩容?新建一个数组,长度是原来的1.5倍,把旧数组内容复制到新数组
Object[] newobj = new Object[length + length / 2];
System.arraycopy(obj, 0, newobj, 0, length);
//旧:hello hello1 hello2
//新:hello hello1 hello2 null
newobj[length] = o;
obj = newobj;
index++;
length = length + length / 2;
} else {
obj[index] = o;
index++;
}
}
public void show() {
for (Object o : obj) {
System.out.println(o);
}
}
public int size() {
return index;
}
public Object get(int i) {
return obj[i];
}
public boolean contain(Object hello) {
for (Object o : obj) {
if(o!=null){
if (o.equals(hello)) {
return true;
}
}
}
return false;
}
public boolean remove(int i) {
if(i>index){
return false;
}else{
Object[] newobj=new Object[length];
System.arraycopy(obj,0,newobj,0,i);
System.arraycopy(obj,i+1,newobj,i,length-i-1);
obj=newobj;
index--;
return true;
}
}
}
测试ListCopy
public class Main {
public static void main(String[] args) {
ListCopy listCopy = new ListCopy(3);
listCopy.add("hello");
listCopy.add("hello1");
listCopy.add("hello2");
listCopy.add("hello4");
boolean f=listCopy.remove(1);
listCopy.show();
boolean flag = listCopy.contain("hello1");
System.out.println(flag);
int size = listCopy.size();
String s = (String) listCopy.get(3);
System.out.println(s);
System.out.println(size);
}
}
案例2:模拟KTV点歌系统
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Test {
private static List<String> list = new ArrayList<>();
public static void main(String[] args) {
//模拟KTV点歌系统
//歌曲列表 置顶 移到上一首 移除歌曲 添加
initsong();
mainScreen();
}
private static void mainScreen() {
System.out.println("=======欢迎来到KTV========");
System.out.println("1.查看列表");
System.out.println("2.置顶歌曲");
System.out.println("3.移到上一首");
System.out.println("4.移除歌曲");
System.out.println("5.添加歌曲");
System.out.println("0.退出系统");
Scanner scanner = new Scanner(System.in);
System.out.println("输入您的操作:");
int i = scanner.nextInt();
switch (i) {
case 0:
loginout();
break;
case 1:
show();
break;
case 2:
totop();
break;
case 3:
up();
break;
case 4:
delete();
break;
case 5:
addsong();
break;
}
}
private static void initsong() {
list.add("两只老虎");
list.add("光辉岁月");
list.add("长城");
list.add("心太软");
mainScreen();
}
private static void delete() {
System.out.println("输入要删除的歌曲:");
Scanner scanner = new Scanner(System.in);
String song = scanner.nextLine();
list.remove(song);
mainScreen();
}
private static void up() {
System.out.println("输入要移动的歌曲:");
Scanner scanner = new Scanner(System.in);
String song = scanner.nextLine();
int i = list.indexOf(song.trim());
if (i < 0) {
System.out.println("所移动歌曲不存在");
} else if (i == 0) {
System.out.println("歌曲已经在顶部!");
} else {
list.remove(song.trim());
list.add(i - 1, song);
show();
}
mainScreen();
}
private static void totop() {
System.out.println("输入置顶歌曲:");
Scanner scanner = new Scanner(System.in);
String song = scanner.nextLine();
list.remove(song.trim());
list.add(0, song);
show();
mainScreen();
}
private static void show() {
System.out.println(list);
mainScreen();
}
private static void addsong() {
System.out.println("输入添加歌曲:");
Scanner scanner = new Scanner(System.in);
String song = scanner.nextLine();
list.add(song.trim());
mainScreen();
}
private static void loginout() {
System.out.println("系统退出");
System.exit(0);
}
}
4.Set接口
1).Set接口介绍
Set是Collection子接口;
Set和Collection基本上一样,一点除外:Set无法记住添加的顺序,不允许包含重复的元素。
当试图添加两个相同元素进Set集合,添加操作失败,add()方法返回false。
Setr如何判断两个对象是否相等?HashSet 集合的add()方法底层依赖于双列集合HashMap, 它依赖于两个方法 equals()和hashCode(); 先比较元素equals(), 再比较hashCoede值.
也就是说两个对象equals比较返回true,Set集合是不会接受这个两个对象的。
常用子类:
HashSet:散列存放
TreeSet:有序存放
2).HashSet集合
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找性能。
HashSet 具有以下特点:
- 不能保证元素的排列顺序
- HashSet 不是线程安全的
- 集合元素可以是 null
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。
3).TreeSet集合
TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。
TreeSet 支持两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序。
自然排序
TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序排列
如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 接口。
实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过 compareTo(Object obj) 方法的返回值来比较大小。
Comparable 的典型实现:
BigDecimal、BigInteger 以及所有的数值型对应的包装类:按它们对应的数值大小进行比较
Character:按字符的 UNICODE 值来进行比较
Boolean:true 对应的包装类实例大于 false 对应的包装类实例
String:按字符串中字符的 UNICODE 值进行比较
Date、Time:后边的时间、日期比前面的时间、日期大
自然排序步骤:
1.让元素自身具备比较性,
2.实现Compareable接口,覆盖其CompareTo方法
public int compareTo(Object obj)
{
if(!(obj instanceof Student)) {
throw new RuntimeException("不是学生对象");}
Student s = (Student)obj;
System.out.println(this.name+"....compareto....."+s.name);
if(this.age>s.age)
return 1;
if(this.age==s.age)
{
return this.name.compareTo(s.name);
}
return -1;
}
定制排序
如果需要实现定制排序,则需要在创建 TreeSet 集合对象时,提供一个 Comparator 接口的实现类对象。由该 Comparator 对象负责集合元素的排序逻辑
定制排序步骤:
1)创建比较器,实现comparator接口
2)复写compare方法
3)在创建TreeSet集合对象时,提供一个一个Comparator对象
class MyComparator implements Comparator{//第一步:实现Comparator接口
public int compare(Object o1, Object o2) {//第二步:实现一个campare方法
if(o1 instanceof Student1 & o2instanceof Student1){
Student1 s1 =(Student1)o1;
Student1 s2 =(Student1)o2;
if(s1.getAge() > s2.getAge()){
return -1;
}else if(s1.getAge() < s2.getAge()){
return 1;
} }
return 0;
}}
Set s= new TreeSet(new MyComparator());//第三步:创建TreeSet集合对象时,提供一个一个Comparator对象
5.Map接口
1).Map接口介绍
Map 用于保存具有映射关系的数据,因此 Map 集合里保存着两组值,一组值用于保存 Map 里的 Key,另外一组用于保存 Map 里的 Value
Map 中的 key 和 value 都可以是任何引用类型的数据
Map 中的 Key 不允许重复,即同一个 Map 对象的任何两个 Key 通过 equals 方法比较中返回 false
Key 和 Value 之间存在单向一对一关系,即通过指定的 Key 总能找到唯一的,确定的 Value。
2).HashMap集合
HashMap是 Map 接口使用频率最高的实现类。
HashMap 是基于哈希表的 Map 接口的非同步实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。与HashSet一样,此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCode 值也相等。
HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。
3).遍历HashMap
①Map集合遍历键找值方式:即通过元素中的键,获取键所对应的值
操作步骤:
1.获取Map集合中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。
2.遍历键的Set集合,得到每一个键
3.根据键,获取键所对应的值
public class Main {
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
map.put("007","hello");
map.put("002","hello2");
map.put("003","hello3");
map.put("004","hello4");
map.put("005","hello5");
Set<String> keys = map.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()){
String key=iterator.next();
System.out.println(key+":"+map.get(key));
}
}
}
②Map集合遍历键值对方式:即通过集合中每个键值对(Entry)对象,获取键值对对象中的键与值
操作步骤:
1.获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。
2.遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象
3.通过键值对(Entry)对象,获取Entry对象中的键与值。
public class Main {
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
map.put("007","hello");
map.put("002","hello2");
map.put("003","hello3");
map.put("004","hello4");
map.put("005","hello5");
Set<Map.Entry<String, String>> entries = map.entrySet();
Iterator<Map.Entry<String, String>> iterator = entries.iterator();
while(iterator.hasNext()){
Map.Entry<String, String> entry=iterator.next();
System.out.println(entry.getKey()+" :" +entry.getValue());
}
}
}
3).TreeMap集合
TreeMap是用键来进行升序顺序来排序的。通过Comparable 或 Comparator来排序。
package pm;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class Test1 {
public static void main(String[] args) {
//输入一串字符串,然后输出每个字符出现的次数,用TreeMap
String s="aklsdhajhfakjhgdasdgahg中华787人民共45678和国";
Map<Character,Integer> map=new TreeMap<>();
char[] chars = s.toCharArray();
for(int i=0;i<chars.length;i++){
if(map.get(chars[i])==null){
map.put(chars[i],1);
}else{
map.put(chars[i],map.get(chars[i])+1);
}
}
Set<Map.Entry<Character, Integer>> entries = map.entrySet();
Iterator<Map.Entry<Character, Integer>> iterator = entries.iterator();
while (iterator.hasNext()){
Map.Entry<Character,Integer> entry=iterator.next();
System.out.println(entry.getKey()+":"+entry.getValue());
}
}
}
4).Properties集合
Properties 类是 Hashtable 的子类,该对象用于处理属性文件
由于属性文件里的 key、value 都是字符串类型,所以 properties 里的 Key 和 Value 都是字符串类型的
案例7-3:斗地主小游戏之洗牌发牌
package pm;
import java.util.*;
public class Tset3 {
public static void main(String[] args) {
//斗地主发牌
//先把牌弄出来,2个集合,一个集合装花色,一个集合装数字
List<String> type=new ArrayList<>();
type.add("♥");
type.add("♠");
type.add("♦");
type.add("♣");
List<String> typecount=new ArrayList<>();
for(int i=3;i<=10;i++){
typecount.add(""+i);
}
//把非数字牌添加进来
typecount.add("J");
typecount.add("Q");
typecount.add("K");
typecount.add("A");
typecount.add("2");
//创建牌的map,每张牌都有一个编号,编号从0-53,把每张牌装进去
Map<Integer,String> poke=new HashMap<>();
int index=0;
for(String s:typecount){
for(String sc:type){
poke.put(index,s+sc);
index++;
}
}
poke.put(52,"大王");
poke.put(53,"小王");
System.out.println(poke);
//创建一个牌编号集合,以便洗牌
List<Integer> number=new ArrayList<>();
for(int i=0;i<54;i++){
number.add(i);
}
//洗牌,编号乱了
Collections.shuffle(number);
//需要4个集合来装牌对应的编号,3个玩家,1个底牌
List<Integer> p1=new ArrayList<>();
List<Integer> p2=new ArrayList<>();
List<Integer> p3=new ArrayList<>();
List<Integer> buttonpoke=new ArrayList<>();
//发牌对应的编号
for(int i=0;i<54;i++){
if(i>=51){
buttonpoke.add(number.get(i));
}else{
if(i%3==1){
p1.add(number.get(i));
}else if(i%3==2){
p2.add(number.get(i));
}else if(i%3==0){
p3.add(number.get(i));
}
}
}
//对编号排序
Collections.sort(p1);
Collections.sort(p2);
Collections.sort(p3);
Collections.sort(buttonpoke);
//需要4个集合来装牌
List<String> p11=new ArrayList<>();
List<String> p22=new ArrayList<>();
List<String> p33=new ArrayList<>();
List<String> buttonpoke1=new ArrayList<>();
//根据编号去牌库获取对应的牌
for(Integer i:p1){
p11.add(poke.get(i));
}
for(Integer i:p2){
p22.add(poke.get(i));
}
for(Integer i:p3){
p33.add(poke.get(i));
}
for(Integer i:buttonpoke){
buttonpoke1.add(poke.get(i));
}
System.out.println("玩家1:"+p11);
System.out.println("玩家2:"+p22);
System.out.println("玩家3:"+p33);
System.out.println("底牌:"+buttonpoke1);
}
}
6.总结
1)Set集合和List集合的区别?
Set: 不允许元素重复, 集合元素唯一(元素可以为null), 不能保证迭代顺序恒久不变, 无序(存储和取出不一致).
List: 允许元素重复, 并且元素有序(存储和取出一致).
2).Set 集合存储元素时可以保证元素的唯一性, 原因什么?
HashSet 集合的add()方法底层依赖于双列集合HashMap, 它依赖于两个方法 equals()和hashCode(); 先比较元素hashCoede值, 再比较equals().
3).HashSet元素如何添加?
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,判断已经存储在集合中的对象的hashCode值是否与添加的对象的hashCode值一致:若不一致:直接添加进去;若一致,再进行equals方法比较,equals方法如果返回true,表明对象已经添加进去了,就不会再添加新的对象了,否则添加进去;如果我们重写了equals方法,也要重写hashCode方法,反之亦然;。
HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode方法返回值也相等。
4).ArrayList和LinkedList有什么区别?
5).HashMap和HashTable有什么区别?