集合的选型
在开发中使用什么集合实现类,取决于业务的特点,然后进行选型。
首先需要判断存储的数据是单列的还是双列的
- 单列的:Collection接口
- 允许重复:List接口
一增删多:LinkedList[底层维护了一个双向链表]
一改查多:ArrayList[底层维护了Object类型的可变数组] - 不允许重复:Set接口
一无序:HashSet[底层是HashMap,维护了一个哈希表(数组+链表+红黑树)]
一有序:TreeSet
一插入和取出顺序一致:LinkedHashSet[底层维护了数组+双向链表]
- 双列的:Map接口
1.键无序:HashMap[底层是哈希表(数组+链表+红黑树)]
2.键排序:TreeMap
3.键插入和取出的顺序一致:LinkedHashMap
4.读取文件Properties
TreeSet
TreeSet也是实现了Set接口的一个实现类,他的主要特点是排序。
TreeSet集合底层实际上是一个TreeMap,而TreeMap集合底层是一个二叉树,也将TreeSet集合中的元素称为可排序组合
下面重点分析,是怎么实现排序的。
重点操作 使用有参构造器Comparator 的compare 方法,写一个匿名内部类作为形参在构造器
具体的排序方法由compare 自己定义
举例说明:String类型的排序
- 使用Comparator 匿名内部类作为传参写在构造器的形参列表,具体对比方法自己写
- 例如要使用String类型的长度作为排序就先向下转型为String,然后o1.length-02length
- 这样的话当使用add方法添加时,底层会调用匿名内部类Comparator的compare 方法,根据return的结果调整位置(底层是二叉树,所以调整的是树左右连接的位置)
- 当返回结果为大于0时,会调整集合数据的位置
- 当结果相等时就不添加了
public class TreeSet_ {
public static void main(String[] args) {
TreeSet treeSet =new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).length()-((String)o2).length();
}
});
treeSet.add("123");
treeSet.add("12");
treeSet.add("1");
System.out.println(treeSet);
}
}
排序结果:
[1, 12, 123]
TreeMap
TreeMap是Map的实现类,基本用法和上面的TreeSet差不多
有一点需要注意,当排序对比return相等时,key和TreeSet一样不会替换,但是value会替换,因为TreeSet是单列集合所以不重要,但是TreeMap是双列集合,需要注意一下这点
Collections工具类
Collections类是一个操作Set,List,Map等集合的工具类
比如对集合进行排序,查询,修改等等…
Collections种提供了一系列的静态的方法来帮助开发这实现对集合的操作
- 排序方法
- reverse(List):反转List中的元素顺序
- shuffle(List):对List集合元素进行随机排序
- sort(List):根据元素的自然顺序对指定的LiST集合元素按照升序排序
- sort(List,Comparator):根据指定的Comparator比较器产生的顺序对List集合元素进行排序
- swap(List,int i,int j):将指定List集合中的i元素和j处元素进行交换
使用演示:
public class test2 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("tim");
list.add("mary");
list.add("jack");
list.add("mile");
// 1. reverse(List):反转List中的元素顺序
Collections.reverse(list);
System.out.println(list);
// 2. shuffle(List):对List集合元素进行随机排序
for (int i = 0; i < 5; i++) {
Collections.shuffle(list);
System.out.println(list);
}
// 3. sort(List):根据元素的自然顺序对指定的LiST集合元素按照升序排序
Collections.sort(list);
System.out.println(list);
// 4. sort(List,Comparator):根据指定的Comparator比较器产生的顺序对List集合元素进行排序
Collections.sort(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).length()-((String)o2).length();//按照长度排序
}
});
System.out.println(list);
// 5. swap(List,int i,int j):将指定List集合中的i元素和j处元素进行交换
Collections.swap(list,0,3);
System.out.println(list);
}
}
输出结果:
[mile, jack, mary, tim]
[jack, mary, mile, tim]
[tim, mary, jack, mile]
[mary, tim, jack, mile]
[mary, mile, jack, tim]
[mary, jack, tim, mile]
[jack, mary, mile, tim]
[tim, jack, mary, mile]
[mile, jack, mary, tim]
- 查找替换
- max(Collection):根据元素的自然顺序返回集合中最大的元素
- max(Collection,Comparator):根据Comparator比较器的结果返回集合中最大的元素
- min(Collection):返回集合中最小的元素
- min(Collection,Comparator):根据Comparator比较器的结果返回集合中最小的元素
- frequency(Collection,Object):返回集合中Object出现的次数
- copy(List dest,List src):将src的数据复制到dest中
- replaceAll(List list,Object oldVal,Object newVal):将list集合中的oldval替换成newval
使用演示
public class test2 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("tim");
list.add("mary");
list.add("jack");
list.add("mile");
list.add("mile");
// 1. max(Collection):根据元素的自然顺序返回集合中最大的元素
System.out.println(Collections.max(list));
// 2. max(Collection,Comparator):根据Comparator比较器的结果返回集合中最大的元素
System.out.println(Collections.max(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).length()-((String)o2).length();
}
}));
// 3. min(Collection):返回集合中最小的元素
System.out.println(Collections.min(list));
// 4. min(Collection,Comparator):根据Comparator比较器的结果返回集合中最小的元素
System.out.println(Collections.min(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).length()-((String)o2).length();
}
}));
// 5. frequency(Collection,Object):返回集合中Object出现的次数
System.out.println(Collections.frequency(list,"mile"));
// 6. copy(List dest,List src):将src的数据复制到dest中
ArrayList list2 = new ArrayList();
for (int i = 0; i < list.size(); i++) {//先给新集合开一样大的空间,否则会报错
list2.add("");
}
Collections.copy(list2,list);
System.out.println(list2);
// 7. replaceAll(List list,Object oldVal,Object newVal):将list集合中的oldval替换成newval
Collections.replaceAll(list,"mile","lll");
System.out.println(list);
}
}
输出结果:
tim
mary
jack
tim
2
[tim, mary, jack, mile, mile]
[tim, mary, jack, lll, lll]
集合练习
- 编写实现下面要求的程序
1):封装一个新闻类,包含标题和内容属性,提供get,set方法,重写toString方法,打印对象时只打印标题
2):只提供一个戴参数的构造器,且实例化对象时只初始化标题,且实例化两个对象
新闻标题1:新冠确诊病例超千万,数百万印度角度赴恒河“圣浴”引民众担忧
新闻标题2:男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生
3):将新闻对象添加到ArrayList集合中,且进行倒序遍历
4)在遍历集合的过程中,对新闻标题进行处理,超过15个字的只保留前15个,然后再后边加上“…”
5)再控制台打印遍历出经过处理的新闻标题。
public class HomeWork3 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(new News("新冠确诊病例超千万,数百万印度角度赴恒河“圣浴”引民众担忧"));
list.add(new News("男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生"));
for (int i = list.size()-1; i >=0; i--) {//倒叙打印
News news = (News) list.get(i);
System.out.println(protitle(news.title));;
}
}
public static String protitle(String itile){
if (itile == null){
return null;
}
if (itile.length()>15){
return itile.substring(0,15)+"...";
}
return itile;
}
}
class News{
String title;
String content;
public News(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "News{" +
"title='" + title + '\'' +
'}';
}
}
运行结果:
男子突然想起2个月前钓的鱼还在…
新冠确诊病例超千万,数百万印度…
- 使用ArrayList完成对 对象Car{name,price}的各种操作
1).add:添加单个元素
2):remove:删除指定元素
3):contains:查找元素是否存在
4):size:获取元素个数
5):isEmpty:判断是否为空
6):clear:清空
7):addAll:添加多个元素
8);containsAll:查找多个元素是否都存在
9);removeAll:删除多个元素
使用增强for和迭代器来遍历所有car,需要重写Car的toString方法
/*
*@author WINorYU
*/
public class HomeWork5 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
// 1).add:添加单个元素
list.add(new Car("宝马",250000));
list.add(new Car("宾利",5500000));
Car c = new Car("BJ40",180000);
list.add(c);
// 2):remove:删除指定元素
list.remove(1);
System.out.println(list);
// 3):contains:查找元素是否存在
System.out.println(list.contains(c));
// 4):size:获取元素个数
System.out.println(list.size());
// 5):isEmpty:判断是否为空
System.out.println(list.isEmpty());
// 6):clear:清空
list.clear();
System.out.println(list);
// 7):addAll:添加多个元素
ArrayList list2 = new ArrayList();
list2.add(c);
list2.add(new Car("宾利",5500000));
list.addAll(list2);
// 8);containsAll:查找多个元素是否都存在
System.out.println(list.containsAll(list2));
// 9);removeAll:删除多个元素
list.removeAll(list2);
System.out.println(list);
// 使用增强for和迭代器来遍历所有car,需要重写Car的toString方法
//先加几个进去
list.add(new Car("宝马",250000));
list.add(new Car("宾利",5500000));
list.add(c);
for (Object o1:list) {
System.out.println(o1);
}
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
}
}
class Car{
String name;
double price;
public Car(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
- 按要求完成下列任务
1)使用HashMap类实例化一个Map类型的对象m,键和值分别用于存储员工的姓名和工资,存入数据如下 jack-650元,tom-1200元,smith-2900元
2)将jack的工资改为2600元
3)为所有员工的工资加100元
4)遍历集合中所有的员工和工资
public class HomeWork6 {
public static void main(String[] args) {
HashMap map = new HashMap();
map.put("jack",650);
map.put("tom",1200);
map.put("smith",2900);
//修改jack的工资
map.put("jack",2600);
//为所有的员工工资+100,遍历
Set EntrySet =map.entrySet();
for (Object node:EntrySet) {
Map.Entry entry = (Map.Entry)node;
map.put(entry.getKey(),(Integer)entry.getValue()+100);
System.out.println(entry.getKey()+"-"+entry.getValue());
}
}
}
- 简答题
分析HashSet和TreeSet的去重机制是什么
HashSet的去重机制:hashCode() + equals(),底层先通过将要存入的对象进行运算,得到一个hash值,通过hash值得到要存入的索引位置,如果发现table索引所在的位置没有元素,则直接放入,如果有已经有元素了,则通过equals进行比较{遍历比较},比较后如果不同就加入在后面,相同就不加入
TreeSet的去重机制:如果传入了一个Comparator匿名对象,则以实现的compare去重,如果返回的结果是0则认为是相同的元素,就不添加。反之就添加,如果没有传入Comparator匿名对象,则以将要添加的对象类型实现的Compareable接口的compareTo方法进行去重
- 查看下列代码分析是否有错误,如果有,说明为什么
public class test9{
public static void main(String [] args){
TreeSet treeset = new TreeSet();
treeset.add(new Person());
}
}
class Person{}
会报类型转换异常,ClassCastException,因为实例化TreeSet时没有传入Comparator匿名对象,所以当添加对象时,会自动去调用要添加的对象的compareTo方法,而Person类并没有实现Compareable接口,在底层源码中TreeSet会先将要添加对象向上转型成Compareable,但是Person和Compareable之间没有关系,所以会类型转换异常
- 分析下列代码会输出什么
//已知,Person类按照id和name重写了hashCode方法
@SuppressWarnings({"ALL"})
public class test9{
public static void main(String [] args){
HashSet hashSet = new HashSet();
Person p1 = new Person(1001,"AA");
Person p2 = new Person(1002,"BB");
hashSet.add(p1);
hashSet.add(p2);//加入了两个元素
p1.name = "CC";//将p1的那么改成了“CC”
hashSet.remove(p1);//这里因为remove是根据hash索引来删除的,第一次添加p1是1001-AA,而上面把p1的AA改成了CC,
// 所以hash值不一样了,所以最终找到的索引位置也不一样,从而导致删除失败
System.out.println(hashSet);//因为删除失败所以,还是有两个1001-CC和1002-BB
hashSet.add(new Person(1001,"CC"));//这里还是跟删除一样的道理,根据hash值计算得到索引,因为p1的索引是1001-AA得到的
//虽然后面改成了CC,但是索引位置是没有改的。前面又只是删除1001-CC计算的索引,并没有添加,所以索引自然不会又重复的,最终可以添加成功
System.out.println(hashSet);//输出三个元素
hashSet.add(new Person(1001,"AA"));//这里又添加了一个跟原始p1一样的id和name,所以计算得到的索引会和现在的p1:1001-CC是相同的索引
//但是呢因为现在的p1那么不是AA了而是CC,所以会添加在p1的后面,最终添加成功
System.out.println(hashSet);//输出4个元素
}
}
class Person{
int id;
String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
- 写出ArrayList和Vector的区别
实现类 | 底层结构 | 版本 | 线程安全和效率 | 扩容机制 |
---|---|---|---|---|
ArrayList | 可变数组 | jdk1.2 | 不安全,效率高 | 有参构造器按照1.5倍扩容/无参构造器初始10,后面都是1.5倍 |
Vector | 可变数组 | jdk1.0 | 安全,效率不高 | 有参构造初始按照2倍扩容/无参构造器初始10,后面都是2倍 |