目录
应用场景
集合框架的体系结构
Collection
Map
集合和数组的区别
List概述
Set概述
HashSet
Iterator(迭代器)
hashCode 和 equals
关于getClass()和.class的作用
自定义对象
删除元素
应用场景
- 无法预测存储数据的数量
- 同时存储具有一对一关系的数据
- 需要进行数据的增删改查
- 需要解决数据重复问题,可以直接使用集合Set完成
集合框架的体系结构
Collection
主要用于存储类的对象。
Collection下有三个子接口,分别是List,Queue和Set,List和Queue中可以存储有序且重复的数据,Set中存储的数据是无序且不允许重复。
- List接口的主要实现类包括ArrayList和LinkedeList,LinkedList同时实现了Queue接口
- ArrayList的底层实现是数组,因此在内存中是连续存储的。查询速度快,但增加和删除速度慢。
- LinkedList底层是基于双向链表的,增加和删除速度快,查询速度慢。
- Set接口主要实现类有HashSet和TreeSet
- HashSet是基于哈希表实现的,数据是无序的,HashSet元素可以是null,但只能有一个null。
- TreeSer基于二叉树实现的,可以实现数据的自动排序,确保集合元素处于排序状态,不允许放入空值。
- HashSet的性能优于TreeSet,一般情况下建议使用HashSet,如果需要使用排序功能建议使用TreeSet。
Map
主要用于存储键值对的数据。
Map的主要实现类包括HashMap和TreeMap,其中HashMap基于哈希表实现,TreeMap基于红黑树实现。
- HashMap适用于在Map中插入、删除和定位元素。
- TreeMap适用于按自然顺序或自定义顺序对键值进行遍历
- HashMap比TreeMap性能好,所以HashMap使用更多一些,如果需要对数据经排序可以使用TreeMap。
-
集合和数组的区别
- 数组的长度是固定的,集合的长度可以动态扩展。
- 数组只能存储相同数据类型的数据,而集合可以存储不同数据类型的数据。
- 集合加上范型,也就限制了集合中只能存储这一种数据类型的数据。
- 数组可以存储基本数据类型数据和引用类型数据,而集合只能是引用类型。
public class list {public static void main(String[] args){List list = new ArrayList();list.add(new String("hello"));// 至于我们看到list.add(1)也没报错,是因为自动装箱操作,将基本数据类型转换为对应的包装器类对象了。list.add(1);list.add(new Double(10));System.out.println("********************");for(int i = 0; i < list.size(); i++){System.out.println("" + list.get(i) +" , "+ list.get(i).getClass());}System.out.println("********************");}
}
输出:
********************
hello , class java.lang.String
1 , class java.lang.Integer
10.0 , class java.lang.Double
********************
从上面我们可以看出,可以存放不同类型的对象,至于我们看到list.add(1)也没报错,是因为自动装箱操作,将基本数据类型转换为对应的包装器类对象了。
List概述
List
- List是元素有序并且可以重复的集合,称为序列
- List可以精确的控制每个元素的插入位置,或删除某个位置的元素
- List的两个主要实现类是ArrayList和LinkedList
ArrayList
- 底层是有数组实现的
- 动态增长,以满足应用程序的需求
- 在列表尾部插入或删除非常有效
- 更适合查找和更新元素
- ArrayList中的元素可以为null
下面是leetcode上的一个例题,关于List的元素任然是List,以及增强型for循环使用,当然也有下标索引取值的方法
public class Generate118 {public static List<List<Integer>> generate(int numRows) {List<List<Integer>> ans = new ArrayList<>();for(int i = 0; i < numRows; i++){List<Integer> temp = new ArrayList<>((i + 1));if(i == 0){temp.add(1);ans.add(temp);}else{temp.add(1);for(int j = 1; j < i; j++){temp.add(ans.get(i - 1).get(j - 1) + ans.get(i - 1).get(j));}temp.add(1);ans.add(temp);}}return ans;}public static void main(String[] args){List<List<Integer>> ans = generate(5);for(List<Integer> temp: ans){for(int one: temp){System.out.print(one + " ");}System.out.println();}}
}
输出
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
Set概述
Set是元素无序并且不可以重复的集合,被称为集。
HashSet
- HashSet是Set的一个重要实现类,称为哈希集
- HashSet中的元素无序并且不可以重复
- HashSet中只允许一个null元素
- 具有良好的存取和查找性能
- 若我们尝试向hashSet里面添加重复的元素,并不会报错,只不过hashSet中不会多出一个相同元素。
- HashSet底层是基于HashMap实现的
Iterator(迭代器)
- Iterator接口可以以统一的方式对各种集合元素进行遍历
- hasNext()方法检测集合中是否还有下一个元素
- next()方法返回集合中的下一个元素
- 迭代器在使用时要注意,迭代器是不断向后走的,如果需要多次使用一个迭代器,需要每次使用前都要把迭代器指向set的初始位置。it=catSet.iterator();
public class WordDemo {public static void main(String[] args){// 将英文单词添加到HashSet中Set words = new HashSet();// 向集合中添加元素words.add("blue");words.add("red");words.add("black");words.add("white");pringInfo(words, "");// 在集合中插入一个新的单词
// words.add("green");words.add("white"); // 并不会报错,只是不会插入pringInfo(words, "插入重复元素后,");}public static void pringInfo(Set words, String operator){// 显示集合中的元素System.out.println(operator + "集合中的元素个数为:" + words.size());Iterator it = words.iterator();// 遍历迭代器并输出元素while(it.hasNext()){System.out.print(it.next() + " , ");}System.out.println("\n=========================\n");}
}
输出
集合中的元素个数为:4
red , blue , white , black ,
=========================插入重复元素后,集合中的元素个数为:4
red , blue , white , black ,
=========================
hashCode 和 equals
hashCode()方法用于给对象返回hash code值,equals()方法用于判断其他对象与该对象是否相等。为什么需要这两个方法呢?我们知道HashSet中是不允许添加重复元素的,那么当调用add()方法向HashSet中添加元素时,是如何判断两个元素是不同的。这就用到了hashCode()和equals()方法。在添加数据时,会调用hashCode()方法得到hash code值,通过这个值可以找到数据存储位置,该位置可以理解成一片区域,在该区域存储的数据的hashCode值都是相等的。如果该区域已经有数据了,就继续调用equals()方法判断数据是否相等,如果相等就说明数据重复了,就不能再添加了。如果不相等,就找到一个位置进行存储。
这些是基于哈希算法完成的,它使得添加数据的效率得到了提升。假设此时Set集合中已经有两个100个元素,那么如果想添加第101个元素,如果此时没有使用哈希算法,就需要调用equals()方法将第101个元素与前100个元素依次进行比较,如果元素更多,比较所耗费的时间就越长。
如果两个对象相等,那么他们的hashCode值一定相等。反之,如果两个对象的hashCode值相等,那么这两个对象不一定相等,还需要使用equals()方法进行判断。
如果不重写hashCode()方法,默认每个对象的hashCode()值都不一样,所以该类的每个对象都不会相等 。
关于getClass()和.class的作用
@Override
public boolean equals(Object obj) {// 判断对象是否相等,相等则返回true,不用继续比较属性了if(this == obj)return true;// 判断obj是否是Cat类的对象if(obj.getClass() == Cat.class){Cat temp = (Cat) obj;if(this.hashCode() == temp.hashCode())return (getName().equals(temp.getName())&& (getMonth() == temp.getMonth())&& getSpecies().equals(temp.getSpecies()));}return false;
}
关于if(obj.getClass() == Cat.class)
首先,来看一下Class类。在Java中,万事万物皆对象,每个类都有一个相应的Class对象。通过Class类,可以获得一个类的基本信息,比如属性、方法和构造方法等。这些都属于Java反射的内容,在后面的课程中将会学习到。
getClass()是Object类的方法,该方法的返回值类型是Class类,通过getClass()方法可以得到一个Class类的对象。而.class返回的也是Class类型的对象。所以,如果obj.getClass()和Cat.class返回的内容相等,说明是同一个对象。
既然都可以得到Class的对象,关于getClass()和.class的区别:getClass()方法,有多态能力,运行时可以返回子类的类型信息。.class是没有多态的,是静态解析的,编译时可以确定类型信息。
下面我们来说说,为什么不用if(obj isinstanceof Cat),obj.getClass()==Cat.class与obj instanceof Cat并不是等价的。obj.getClass==Cat.class就是判断obj是Cat类型的,而obj instanceof Cat中obj可以是Cat子类的对象。若用obj isinstanceof Cat来判断,则只要obj是Cat或其子类,且这三个属性相等就会被判为相等,其实他们都有可能不是同一类型的对象,而obj.getClass()是有多态能力,运行时可以返回子类的类型信息,.class是没有多态的,是静态解析的,此时若obj是Cat的子类就不可以了,保证了他们是同一类型的对象。
自定义对象
关于自定义对象,若我们想利用Set的不能添加重复元素的属性,则必须重写洗定义对象的hashCode方法和equals方法,至于之前用String类型的对象没有重写,是因为String类已经帮我们重写过了。
public class Cat {private String name;private int month;private String species;public Cat(String name, int month, String spcies) {this.name = name;this.month = month;this.species = spcies;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getMonth() {return month;}public void setMonth(int month) {this.month = month;}public String getSpecies() {return species;}public void setSpecies(String spcies) {this.species = spcies;}@Overridepublic String toString() {return "Cat{" +"name='" + name + '\'' +", month=" + month +", spcies='" + species + '\'' +'}';}@Overridepublic boolean equals(Object obj) {// 判断对象是否相等,相等则返回true,不用继续比较属性了if(this == obj)return true;// 判断obj是否是Cat类的对象if(obj.getClass() == Cat.class){Cat temp = (Cat) obj;if(this.hashCode() == temp.hashCode())return (getName().equals(temp.getName())&& (getMonth() == temp.getMonth())&& getSpecies().equals(temp.getSpecies()));}return false;}@Overridepublic int hashCode() {final int prime = 33;int result = 1;result = prime * result + month;result = prime * result + ((name == null) ? 0: name.hashCode());result = prime * result + ((species == null) ? 0: species.hashCode());return result;}
}
public class CatTest {public static void main(String[] args){Cat huahua = new Cat("花花", 12, "英短");Cat fanfan = new Cat("凡凡", 3, "中华田园猫");// 将宠物猫对象放入HashSet中Set cats = new HashSet();cats.add(huahua);cats.add(fanfan);printInfo(cats, "");// 再添加一个与花花属性一样的猫Cat huahua_copy = new Cat("花花", 12, "英短");// 添加重复数据cats.add(huahua_copy);printInfo(cats, "");}public static void printInfo(Set cats, String operator){// 显示集合中的元素System.out.println(operator + "集合中的元素个数为:" + cats.size());Iterator it = cats.iterator();// 遍历迭代器并输出元素while(it.hasNext()){
// Cat cat = (Cat)it.next();
// System.out.print(cat.getName() + " , ");System.out.print((Cat)it.next() + " , ");}System.out.println("\n=========================\n");}
}
输出:重写了hashCode和equals方法,则就不会添加重复元素了。
集合中的元素个数为:2
Cat{name='花花', month=12, spcies='英短'} , Cat{name='凡凡', month=3, spcies='中华田园猫'} ,
=========================集合中的元素个数为:2
Cat{name='花花', month=12, spcies='英短'} , Cat{name='凡凡', month=3, spcies='中华田园猫'} ,
=========================
删除元素
/**
* Java中是不允许一边遍历一遍进行修改集合中的元素,会出现数据的不一致性的问题。
* 对于Set集合,如果删除的是集合中最后一个元素,是不会进行报错的。
* 而在删除数据后,集合的存储结构就发生变化了,如果再遍历就会出错的。
* 解决方案是:在删除数据后,使用break;语句跳出循环。
*/
// 删除花花二代的信息,若只有一条符合要求
for(Cat oneCat: cats){if(oneCat.getName().equals("花花二代")){cats.remove(oneCat);break;}
}
printInfo(cats, "删除花花二代后的,");// 若多条符合要求,用新的HashSet来存储需要被删除的对象,再通过removeAll
Set<Cat> removeCats = new HashSet<Cat>();
for(Cat oneCat: cats){if(oneCat.getMonth() < 5){removeCats.add(oneCat);}
}
cats.removeAll(removeCats);
printInfo(cats, "删除小于5个月后的,");
未完,待续
参考: 慕课网-Java工程师