1.简介
1.面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。数组虽然也可以存储对象,但长度是固定的;集合长度是可变的,数组中可以存储基本数据类型,集合只能存储对象。集合类的特点:集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象
2.集合图
2.Collection接口
1.collection常用方法
- 一组称为元素的对象
- 个Collection中可以放不同类型的数据 (Object)当然你可以指明类型
- 是Set接口和List接口的父类
- Set - 无序的集合;不允许重复(哈希表实现唯一)HashSet
- List - 有序的集合;允许重复 ArrayList(数组实现) LinkedList(链表实现)
3.Set接口
- Collection的子接口
- 用来包含一组 无序无重复 的对象
1.Set接口的实现类
- HashSet — HashSet的特性在于其内部对象的散列存取,即采用哈希技术(不要排序使用)
- TreeSet — TreeSet存入的顺序跟存储的顺序不同,但是存储是按照排序存储的(排序使用)
// 创建集合对象 HashSet:不保证 set 的迭代顺序,特别是它不保证该顺序恒久不变。
// 哈希表由hashCode()和equals()方法来保证元素的唯一性。
Set<String> set = new HashSet<String>();
set.add("hello");
set.add("aaaa");
set.add("java");
set.add("world");
set.add("bbbb");
set.add("aaaa");//已有相同,不会被插入
set.add("aaaa");//已有相同,不会被插入
//[java, world, hello, aaaa, bbbb]
System.out.println(set);
/*
* LinkedHashSet:底层数据结构是哈希表和链表。
* 哈希表:能够保证元素的唯一性
* 链表:能够保证元素有序
*/
public class LinkedHashSetDemo {
public static void main(String[] args) {
LinkedHashSet<String> link = new LinkedHashSet<String>();
link.add("ccc");
link.add("hello");
link.add("bbb");
link.add("world");
link.add("aaa");
link.add("java");
link.add("aaa");
link.add("java");
System.out.println("link:" + link);
//link:[ccc, hello, bbb, world, aaa, java]
}
}
/*
* TreeSet:底层数据结构是二叉树。
* 可以让元素排序,有两种方案,分别是:自然排序,以及Comparator(比较器)进行排序,具体使用哪种情况看你使用的构造方法。
*/
public class TreeSetDemo {
public static void main(String[] args) {
// 20,18,23,22,17,24,19,18,24
// 构造一个新的空 set,该 set 根据其元素的自然顺序进行排序。
TreeSet<Integer> ts = new TreeSet<Integer>();
// 存储元素
ts.add(20);
ts.add(18);
ts.add(23);
ts.add(22);
ts.add(17);
ts.add(24);
ts.add(19);
ts.add(18);
ts.add(24);
// [17, 18, 19, 20, 22, 23, 24]
System.out.println(ts);
}
}
2.使用HashSet储存自定义对象时必须重写equals和hashcode方法
如下例:
a.
public class User {
private String number;
private String name;
private Integer age;
public User() {
}
public User(String number, String name, Integer age) {
this.number = number;
this.name = name;
this.age = age;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"number='" + number + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) {
HashSet<User> set = new HashSet<>();
User user1 = new User("1", "sj", 11);
User user2 = new User("1", "sj", 11);
set.add(user1);
set.add(user2);
System.out.println(set.size());//结果为2,自定义对象hashcode可能会相同
}
}
如上结果为2,两个相同的数据还是添加进去了(因为是两个对象的引用地址并不同,认为是不同的对象,这个就需要我们重写 equals 和 hashCode方法)
HashSet集合对象如何判断数据元素是否重复:
检查待存对象hashCode值是否与集合中已有元素对象hashCode值相同,如
果hashCode不同则表示不重复, 如果hashCode相同再调用equals方法进一步检查
equals返回真表示重复,否则表示不重复。
2.如何解决呢?HashSet是基于 equals 和 hashCode来去重的,只需要重写equals 和 hashCode方法
public class User {
private String number;//学号
private String name; //姓名
private Integer age; //年龄
//无参构造函数
public User() {
}
//3个参数的构造函数
public User(String number, String name, Integer age) {
this.number = number;
this.name = name;
this.age = age;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
//判断传入的是否为同一个对象
if (this == o) return true;
//判断传入的对象是否为空,是不是相同的类
if (o == null || getClass() != o.getClass()) return false;
//强转
User user = (User) o;
//判断学号是否相同
if (number != null ? !number.equals(user.number) : user.number != null) return false;
//判断姓名是否相同
if (name != null ? !name.equals(user.name) : user.name != null) return false;
//判断年龄是否相同
return age != null ? age.equals(user.age) : user.age == null;
}
/**
* Hash值的获取方式
* @return
*/
@Override
public int hashCode() {
//获取学号、姓名、年龄的 hashCode 分别乘以 31
int result = number != null ? number.hashCode() : 0;
result = 100 * result + (name != null ? name.hashCode() : 0);
result = 100 * result + (age != null ? age.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "User{" +
"number='" + number + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) {
HashSet<User> set = new HashSet<>();
User user1 = new User("1", "sj", 22);
User user2 = new User("1", "sj", 22);
set.add(user1);
set.add(user2);
System.out.println(set.size());//结果为一
}
}
4.List接口
- Collection的子接口
- 用来包含一组 有序有重复 的对象
- List中的元素都对应一个整数型的序号,记载其在容器中的位置,可以根据序号存取容器中的元素
1.List有两种主要的集合实现类
- ArrayList 集合数据存储的结构是数组结构。元素增删慢,查找快
- LinkedList 集合数据存储的结构是链表结构。方便元素添加、删除的集合。而 LinkedList 提供了大量首尾操作的方法。它可以非常方便的实现我们数据结构中的常见的Stack(栈)、queue(队列)等。
List:
* ArrayList:
* 底层数据结构是数组,查询快,增删慢
* 线程不安全,效率高
* Vector:
* 底层数据结构是数组,查询快,增删慢
* 线程安全,效率低
* LinkedList:
* 底层数据结构是链表,查询慢,增删快
* 线程不安全,效率高
*
2.List接口常用方法
3.LinkedList增加方法
List -> ArrayList
/*
* List集合的特有功能:
* void add(int index,Object obj):在指定索引位置添加元素
* Object remove(int index):删除指定索引位置的元素,并把删除掉的元素值返回
* Object get(int index):根据给定索引,返回元素
* Object set(int index,E element):修改指定索引处的元素为给定的元素,并返回被修改的值
*/
public class ListDemo {
public static void main(String[] args) {
// 创建集合
// Collection c = new ArrayList();
List list = new ArrayList();
// 测试功能
list.add("hello");
list.add("world");
list.add("java");
// void add(int index,Object obj):在指定索引位置添加元素
// list.add(1, "android");
// list.add(3, "android");
// list.add(4, "android"); //有问题
// Object remove(int index):删除指定索引位置的元素,并把删除掉的元素值返回
// System.out.println("remove:" + list.remove(1));
// Object get(int index):根据给定索引,返回元素
// System.out.println("get:" + list.get(1));
// Object set(int index,E element):修改指定索引处的元素为给定的元素,并返回被修改的值
System.out.println("set:" + list.set(1, "android"));
System.out.println("list:" + list);
}
}
List-> Vector
Vector v = new Vector();
// public void addElement(Object obj)
v.addElement("hello");
v.addElement("world");
v.addElement("java");
// 类似迭代器的方式
Enumeration en = v.elements();
while (en.hasMoreElements()) {
String s = (String) en.nextElement();
System.out.println(s);
}
System.out.println("v:" + v);
}
// 创建集合对象
LinkedList link = new LinkedList();
// 添加元素
link.add("hello");
link.add("world");
link.add("java");
// void addFirst()
// void addLast()
// link.addFirst("android");
// link.addLast("javaee");
// Object removeFirst()
// Object removeLast()
// System.out.println("removeFirst:" + link.removeFirst());
// System.out.println("removeLast:" + link.removeLast());
// Object getFirst()
// Object getLast()
System.out.println("getFirst:" + link.getFirst());
System.out.println("getLast:" + link.getLast());
System.out.println("link:" + link);
}
5.Iterator接口
- Iterator对象称作迭代器,用来方便的实现对容器内的元素进行遍历操作
- 所有实现了Collection接口的集合类都有一个iterator( )方法,返回一个实现了Iterator接口的对象
- Iterator对象实现了统一的一个用来遍历Collection中对象的
- Iterator是为遍历而设计,能够从集合中取出元素和删除元素,但是没有添加元素的功能
1.Iterator常用方法
2.Iterator 的并发修改异常
public class IteratorDemo {
//在 list 集合迭代元素中,对元素进行判断,一旦条件满足就添加一个新元素
public static void main(String[] args) {
//创建 List 集合
List<String> list = new ArrayList<String>();
//给集合中添加元素
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
//迭代集合,当有元素为"abc2"时,集合加入新元素"Jss"
Iterator<String> it = list.iterator();
while(it.hasNext()){
String str = it.next();
//判断取出的元素是否是"abc2",是就添加一个新元素
if("abc2".equals(str)){
list.add("Jss");// 该操作会导致程序出错
}
}
//打印容器中的元素
System.out.println(list);
}
}
运行上述代码发生了错误 java.util.ConcurrentModificationException 这是什么原因呢?
在迭代过程中,使用了集合的方法对元素进行操作。导致迭代器并不知道集合中的变化,容易引发数据的不确定性
并发修改异常解决办法:在迭代时,不要使用集合的方法操作元素。
那么想要在迭代时对元素操作咋办?通过 ListIterator 迭代器操作元素是可以的,ListIterator 的出现,
解决了使用 Iterator 迭代过程中可能会发生的错误情况
public class test {
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
ListIterator<String> it=list.listIterator();
while(it.hasNext()) {
String s=it.next();
if("2".equals(s)){
it.add("4");
}
System.out.println(s);
}
System.out.println(list);
}
}
6.Collections类
- 其实就是针对集合进行操作的工具类
- Collections类是类似于Arrays类的公用工具类 ,它提供了一些static方法供集合类使用或操作集合类
1.Collections类中的
// 创建集合对象
ArrayList<Integer> array = new ArrayList<Integer>();
// 添加元素
array.add(30);
array.add(20);
array.add(50);
array.add(10);
array.add(40);
Collections.sort(array);
System.out.println("array:" + array);
// Integer max = Collections.max(array);
// int index = Collections.binarySearch(array, 30);
7.Map接口
- Map内存储的是键/值对这样以成对的对象组(可以把一组对象当成一个元素),通过“键”对象来查询“值”对象
- Map是不同于Collection的另外一种集合接口
- Map中,key值是唯一的(不能重复),而key对象是与value对象关联在一起的
-
Map接口有两个实现:HashMap — key/value对是按照Hash算法存储的
TreeMap — key/value对是排序(按key排序)存储的基于树实现 -
HashMap (线程不安全,允许将null作为一个entry的key或者value,)哈希表实现
-
TreeMap 对是排序(按key排序)存储的基于树实现
-
HashTable(线程安全,Hashtable不允许用null)
HashMap:
特点:
是 Map 集合的子集合
底层采用哈希表结构
HashMap 集合中的 key 不能重复,通过重写 hashCode() 与 equals()方法来保证键的唯一。
不能保证元素存与取的顺序完全一致
LinkedHashMap:
特点:
是 HashMap 集合的子集合
底层采用哈希表+链表结构
LinkedHashMap 集合中的 key 不能重复,通过重写 hashCode() 与 equals()方法来保证键的唯一。
1.Map接口常用方法
2.HashMap与TreeMap的比较
- HashMap基于哈希表实现。
- TreeMap基于树实现。
- HashMap可以通过调优初始容量和负载因子,优化HashMap空间的使用。
- TreeMap没有调优选项,因为该树总处于平衡状态
- HashMap性能优于TreeMap
3.HashMap与Hashtable的比较
- Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现
- Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的
- HashMap允许将null作为一个entry的key或者value,而Hashtable不允许用null。
Map:元素是按照键值对形式存储的。每一对元素由两部分组成。分别叫键和值
* 键是唯一的,值是可以重复的。
* 所以Map集合的底层数据结构是针对键有效,跟值无关。
*
* Map接口和Collection接口的不同?
* A:Map集合是双列集合;Map集合的键是唯一的,值是可以重复的。其实我们也可以简单的理解为Map集合的键和值是由Set和List组成;数据结构针对键有效。
* B:Collection集合是单列集合;Collection集合的儿子Set是唯一的,List是可以重复的;数据结构针对元素有效。
*
* Map集合的功能:
* A:添加功能
* V put(K key,V value):添加,替换或者修改。键不同,添加到集合。键相同,值替换。
* B:移除功能
* void clear():移除所有映射关系
* V remove(Object key):根据键移除键值对元素,返回的是键对应的值
* C:判断功能
* boolean containsKey(Object key):判断Map集合中是否包含指定的键
* boolean containsValue(Object value):判断Map集合中是否包含指定的值
* boolean isEmpty():判断集合是否为空
* D:获取功能
* Set<Map.Entry<K,V>> entrySet():返回的是键值对对象的Set集合。
* V get(Object key):根据键获取值
* Set<K> keySet():所有的键的集合
* Collection<V> values():所有值的集合
* int size():集合的长度
*/
HashMap -获取key-value -------1
/*
* V get(Object key)
* Set<K> keySet()
* Collection<V> values()
*/
public class MapDemo2 {
public static void main(String[] args) {
// 创建集合对象
Map<String, String> map = new HashMap<String, String>();
// 创建并添加元素
map.put("1", "java");
map.put("2", "php");
map.put("3", "android");
map.put("4", "go");
// V get(Object key)
System.out.println("get:" + map.get("1"));
System.out.println("get:" + map.get("2"));
System.out.println("---------------------");
// Set<K> keySet() 获取Map集合的所有的键的Set集合
Set<String> set = map.keySet();
for (String key : set) {
System.out.println(key);
}
System.out.println("---------------------");
// Collection<V> values() 获取Map集合的所有的值的Collection集合
Collection<String> cons = map.values();
for (String value : cons) {
System.out.println(value);
}
}
}
HashMap -获取key-value -------2 Set<Map.Entry<K,V>> entrySet()(建议使用该方式遍历效率高)
public class MapDemo4 {
public static void main(String[] args) {
// 创建集合对象
Map<String, String> map = new HashMap<String, String>();
// 创建并添加元素
map.put("1", "java");
map.put("2", "php");
map.put("3", "android");
map.put("4", "go");
// 遍历
// 获取所有的键值对对象的Set集合。
Set<Map.Entry<String, String>> set = map.entrySet();
// 遍历键值对对象的Set集合,得到每一个键值对对象。
for (Map.Entry<String, String> me : set) {
// 根据键值对对象去获取键和值。getKey(),getValue()
String key = me.getKey();
String value = me.getValue();
System.out.println(key + "---" + value);
}
}
}