集合的概念
集合类又被称为容器,是java数据结构的实现,专门用来存储其他类的对象。常用的集合有List、Set、Map。其中List和Set继承了Collection接口。
- 说起集合,数组可是它的好兄弟,但是二者又有区别:
- 数组的长度是固定的,集合的长度是可变的。
- 数组用来存放基本类型的数据,集合用来存放对象的引用(即对象的地址)。
- 数组中只能是相同的数据类型,集合则可以存放不同的数据类型。
Collection接口
方法 | 功能 |
---|---|
add(Object obj) | 将指定对象添加到该集合中去 |
remove(Object obj) | 将指定对象从该集合中移除 |
isEmpty() | 返回boolean值,用于判断当前集合是否为空 |
size() | 返回int型值,获取改集合中的元素个数 |
contains(Object obj) | 返回boolean值,用于判断集合中是否存在该元素 |
以上是Collection接口的常用方法,由于List集合和Set集合继承了该接口,故这些方法对List和Set来说是通用的。
- 测试
public class Test {
public static void main(String[] args) {
List list=new ArrayList();
list.add(12);
list.add("哈哈哈");
list.add("冲冲冲");
//打印list
System.out.println(list);
//获取集合元素个数
System.out.println(list.size());
//移除指定元素
list.remove("哈哈哈");
//返回集合是否存在"哈哈哈"
System.out.println(list.contains("哈哈哈"));
}
}
- 运行结果
[12, 哈哈哈, 冲冲冲]
3
false
List集合
特点:元素允许重复,各元素的顺序就是对象插入的顺序
List与数组类似,可以使用索引访问元素。主要的实现类有ArrayList和LinkedList。
ArrayList:
- 底层:实现了可变的数组
- 优点:可根据索引位置对集合进行快速的随机访问,查询速度快
- 缺点:插入对象或删除对象的速度慢
LiskedList:
- 底层:采用链表结构保存对象
- 优点:插入对象或删除对象的速度快,效率高
- 缺点:查询速度慢,不便于访问
除了可以使用Collection接口的所有方法,List接口还定义了以下重要的方法
方法 | 功能 |
---|---|
get(int index) | 获取指定索引位置的元素 |
set(int index,Object obj) | 将集合指定索引位置的元素修改为指定对象 |
add(int index,Object obj) | 将指定对象插入到指定索引位置上 |
remove(int index) | 移除指定位置的元素 |
- 测试
public class Test {
public static void main(String[] args) {
//创建泛型为String的List集合
List<String> list = new ArrayList<>();
list.add("你是谁");
list.add("我又是谁");
list.add("你好呀");
//打印list
System.out.println(list);
//获取指定索引位置的元素
System.out.println("索引位置为1的元素为:" + list.get(1));
//替换指定索引位置的元素
list.set(1,"嘿嘿");
System.out.println(list);
//获取指定索引位置的元素
System.out.println("索引位置为1的元素为:" + list.get(1));
//添加元素到指定位置
list.add(1,"我很好");
System.out.println(list);
}
}
- 运行结果
[你是谁, 我又是谁, 你好呀]
索引位置为1的元素为:我又是谁
[你是谁, 嘿嘿, 你好呀]
索引位置为1的元素为:嘿嘿
[你是谁, 我很好, 嘿嘿, 你好呀]
Set集合
特点:不能包含重复元素,不按特点方式排序
由于Set集合是无序且不重复的,故无法像List一样使用get,set,indexOf等有关元素具体位置的方法。
HashSet:
- 底层:实现Set接口,由哈希表支持
- 特点:不保证Set的迭代顺序
TreeSet:
- 底层:不仅实现了Set接口,还实现了java.util.SortedSet接口
- 特点:在遍历集合时按照自然顺序递增排序,也可以按照指定比较器递增排序
Map集合
Map集合没有继承Collection接口,其提供的是key到value的映射。Map中不允许包含相同的key(key不能重复,否则新的覆盖久的),且每个key只能映射一个value(value只能有一个,否则新的覆盖久的)。
- Map接口中的常用方法
方法 | 功能 |
---|---|
put(K key,V value) | 向集合中添加指定的key和value的映射关系 |
get(Object key) | 如果存在指定的key对象,则返回该对象对应的值,否则返回null |
keySet() | 返回该集合中所有key对象形成的Set集合 |
values() | 返回该集合所有值对象形成的Collection集合 |
containsKey(Object key) | 如果此映射包含指定key的映射关系,则返回true |
containsValue(Object value) | 如果此映射将一个或多个key映射到指定值,则返回true |
HashMap:
- 底层:实现基于哈希表
- 特点:通过哈希表对其内部的映射关系进行快速查找,但不保证映射的顺序
TreeMap:
- 底层:不仅实现了Map接口,还实现了java.util.SortedMap接口
- 特点:集合中的映射关系有一定的顺序,但在添加、删除、定位映射关系时,TreeMap类比HashMap的类的性能稍差
/**
* Map常用操作
*/
public class testMap {
public static void main(String args[]){
//创建person对象
person person1=new person(101,"cjt ",1.81);
person person2=new person(102,"小伙子 ",1.79);
//两种不同的Map
Map<Integer,person> map1=new HashMap<>();
Map<Integer,String> map2=new HashMap<>();
//map1存入person对象
map1.put(1,person1);
map1.put(2,person2);
//map2存入String对象
map2.put(1,"cjt");
//打印集合
System.out.println(map1.get(1));
System.out.println(map1);
System.out.println(map2);
}
}
/**
* person实体类
*/
class person{
int id;
String name;
double tall;
public person(int id,String name,double tall){
this.id=id;
this.name=name;
this.tall=tall;
}
@Override
public String toString() {
return "person{" +
"id=" + id +
", name='" + name + '\'' +
", tall=" + tall +
'}';
}
}
- 运行结果
person{id=101, name='cjt ', tall=1.81}
{1=person{id=101, name='cjt ', tall=1.81}, 2=person{id=102, name='小伙子 ', tall=1.79}}
{1=cjt}
关于hashcode方法
hashcode的参考资料:https://www.cnblogs.com/qlqwjy/p/7731556.html
简介:hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构(如哈希表)中确定对象的存储地址的。凡是把类对象放到以哈希表为内部存储结构的容器中,相应的类必须要实现equals方法和hashCode方法,这样才符合哈希表真实的逻辑功能。
作用:hashcode方法返回对象的哈希码值,也就是对象的内存真实地址的整数化表示。如果两个对象内容相同,则对应的哈希码值也相同,尽管这些对象占用的是不同的内存。如果两个对象equals方法为true,则对应的hashcode值一定相等,因为equals就表示两对象相同。但是如果两对象hashcode值相等,则equals方法不一定为true。
为什么使用hashcode:
考虑一种情况,当向集合中插入对象时,如何判别在集合中是否已经存在该对象了?(注意:集合中不允许重复的元素存在)
也许大多数人都会想到调用equals方法来逐个进行比较,这个方法确实可行。但是如果集合中已经存在一万条数据或者更多的数据,如果采用 equals方法去逐一比较,效率必然是一个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的 hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode 值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用 equals方法的次数就大大降低了,说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的 字段等)映射成一个数值,这个数值称作为散列值。
简而言之:哈希表先判断是否有相同的hashcode值,如果没有,则直接存放进去。如果有,则根据它的hashcode方法提供的哈希码找到存储的位置,调用equals方法从位置所关联的链表里面寻找是否有相同的对象,如果有相同的对象,则不存放,如果没有,则存放进去。
注意事项:如果对象的equals方法被重写,那么对象的hashCode也尽量重写
应用场景:添加到Set集合的对象和添加到Map集合的键。
遍历容器
- 方法一:使用for循环
public class TestList {
public static void main(String args[]){
ArrayList<String> list = new ArrayList<>();
list.add("第一个");
list.add("第二个");
list.add("第三个");
//使用for循环,通过索引遍历
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
//同时也可通过索引删除元素
//list.remove(i);
}
//增强for循环
for(String str : list){
System.out.println(str);
//不能删除元素
}
}
}
public class Test {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"第一个");
map.put(2,"第二个");
map.put(3,"第三个");
//遍历键值
for(int key : map.keySet()){
System.out.println(key);
}
//遍历value
for(String str : map.values()){
System.out.println(str);
}
//同时获取key和value
for(Map.Entry<Integer,String> entry : map.entrySet()){
System.out.println("键:"+entry.getKey()+"值:"+entry.getValue());
}
}
}
- 方法二:使用iterator迭代器遍历
public class TestList {
public static void main(String args[]){
ArrayList<String> list = new ArrayList<>();
list.add("第一个");
list.add("第二个");
list.add("第三个");
Iterator<String> it = list.iterator();
//判断集合是否有下一个元素,返回boolean值
while(it.hasNext()){
//返回下一个元素并使游标后移一位
String str = it.next();
System.out.println(str);
}
}
}
public class Test {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"第一个");
map.put(2,"第二个");
map.put(3,"第三个");
//获取key集合
Set<Integer> keySet = map.keySet();
Iterator<Integer> it1 = keySet.iterator();
while(it1.hasNext()){
System.out.println(it1.next());
}
//获取value集合
Collection<String> values = map.values();
Iterator<String> it2 = values.iterator();
while(it2.hasNext()){
System.out.println(it2.next());
}
//获取键值对
Iterator<Map.Entry<Integer,String>> entries = map.entrySet().iterator();
while(entries.hasNext()){
Map.Entry<Integer,String> entry = entries.next();
System.out.println("键:"+entry.getKey()+"值:"+entry.getValue());
}
}
}
- 方法三:使用lambda遍历
public class TestList {
public static void main(String args[]){
ArrayList<String> list = new ArrayList<>();
list.add("第一个");
list.add("第二个");
list.add("第三个");
list.forEach(s -> {
System.out.println(s);
});
}
}