集合操作(java.util*)
java.util.Collection
--------------->java.util.List
----------->java.util.ArrayList
------------>java.util.LinkedList
------------->java.util.Set
1.java.util.ArrayList
1.1 基本使用
底层操作就是一个数组(添加,移除,扩容)
基本使用 add ,size有效元素
添加元素
ArrayList list = new ArrayList();//不写泛型默认object 容纳任何对象。
Product p1 = new Product("001", "java課程1", 3000);
list.add(p1);
查看有效元素的个数
System.out.println(list.size());
获取某个元素,根据数组下标获取
取第0个元素
System.out.println(list.get(0));
直接打印他的对象相当于打印他的ToString 直接遍历打印每个对象的tostring
System.out.println(list);
添加元素,放在第三个位置可以在但是下标不能大于数组的大小 ,不然就下标越界相当于在数组下标范围内,任意位置插入一个元素。
list.add(3,p1);
如果你是一个聚集,那么就会提供一个遍历和访问元素的方式。这个就和设计模式相关,称为迭代器模式。
删除第3个位置的元素
list.remove(3);
集合的遍历
//遍历方式1
for (int i = 0; i <list.size(); i++) {
System.out.println(list.get(i));
}
方式二
/**遍历方式2迭代器遍历
*如果你是一个聚集,那么就会提供一个遍历和访问元素的方式。
* 并且不暴露集合的实现
* 这个就和设计模式相关,称为迭代器模式。
* ArrayList类已经提供了迭代器
* java.util.Iterator
*/
Iterator itor = list.iterator();
while (itor.hasNext()){
Product pro = (Product) itor.next();
System.out.println(pro);
}
方式三
会把第一个给obj,再把第2个给obj,再把3个给obj一直到最后一个
// 遍历方式三
//foreach 遍历
for(Object obj :list){
System.out.println(obj);
}
//判断某个元素是否包含在集合中用contains 方法
//判断某个元素是否包含在集合中
System.out.println(list.contains(p1));
contains 方法注意和实际场景的使用
可以进行重写hashCode 然后选择那些属性是相同的就是一个对象。
list集合中可以放重复的元素。
ArrayList 源代码
两个重要成员 Object[] EMPTY_ELEMENTDATA = {};
int size;
一个是空数组,一个是存数组长度。
其实数组的初始大小是10;
扩容,后移的操作。
移除元素的时候要做前移操作。
注意:ArrayList 这种有数组最为底层的操作,最好估算元素的多少来指定底层数组的大小。
ArrayList list = new ArrayList(给定大小);
避免数组扩容操作,因为扩容操作会产生垃圾,占用内存,性能较差。
1,2 java.util.Vector
操作都是类似的
Vector<String> vector = new Vector<>();
vector.add("hello");
vector.add("world");
System.out.println(vector.size());
System.out.println(vector);
默认大小也是10;
底层源代码 加了很多synchronized 关键字 ,区别就是一个线程安全一个线程不安全,
遍历方式都是一样,只不过是多了一种
for (int i = 0; i <vector.size() ; i++) {
System.out.println(vector.get(i));
}
for (String str:vector){
System.out.println(str);
}
Iterator<String> itor =vector.iterator();
while (itor.hasNext()){
String s = itor.next();
System.out.println(s);
}
//还有一种遍历方式
Enumeration <String> e=vector.elements();
while (e.hasMoreElements()){
System.out.println(e.nextElement());
}
底层也是数组的操作,大部分功能和ArrayList 类似。 有一种老的遍历方式 类似于 迭代器遍历。
ArrayList 线程不安全
Vector 线程安全
jdk7之前的版本扩容的大小稍有差异。
判断一个集合是否为空 isEmpty()
boolean b = vector.isEmpty();
2,LinkedList (链表结构)
2,1 基本使用和ArrayList 类似
LinkedList<String> list = new LinkedList<>();
list.add("mengzi1");
list.add("mengzi2");
list.add("mengzi3");
list.add("mengzi4");
// 有效元素的个数
System.out.println(list.size());
//移除一个元素
list.remove(2);
//打印对象里的所有内容
System.out.println(list);
//遍历
for (int i = 0; i <list.size() ; i++) {
System.out.println(list.get(i));
}
for (String str :list){
System.out.println(str);
}
Iterator<String> itor = list.iterator();
while (itor.hasNext()){
String next = itor.next();
System.out.println(next);
}
LinkedList 是一个链表结构 提供了一个非常方便的 头尾操作。
不需要元素的移位。直接指向就可以了。
addFirst,addLast,getFirst,getLast
适合做一个 头尾操作比较频繁的事,比如 :贪吃蛇
3,java.util.HashSet 集合
set 集合不能放重复对象,是否重复由equals 方法决定
注意:容纳的对象必须根据自己的唯一标识来重写equals 和hashCode方法。
注意:对象一旦放入HashSet 容器中,对象的标识属性值不能再修改,否则倒置对象移除不了。
HashSet 存放元素的时候元素的位置是和对象hashCode算法得到相关的。
那么去查找元素时,也要根据这个算法的值,然后去找到相关的位置,移除元素,如果
没有元素存在该算法的属性值在存放容器后被修改了,意味着hashCode算法得到和之前的值不一样了,就删除不了,久而久之就会存在内存泄露。
3.2 HashSet 的数据结构
HashSet底层就是HashMap
基本使用添加,移除元素,遍历,和之前的一样。
4.java.util.TreeSet
注意:TreeSet中容纳的对象,必须能够排序(排序的方式自己定义)
java中有两种排序的比较器
4.1 java.lang.Comparable
构造函数如果无参数,那么TreeSet 使用无参的构造函数,那么容纳的对象必须实现 Comparable 接口
package corelesson3;
import java.util.TreeSet;
public class TreeSetDemo1 {
public static void main(String[] args) {
User user1= new User("menzi1","123123",18);
User user2= new User("lisi","12",21);
User user3= new User("lisi","12",20);
TreeSet<User> ts = new TreeSet<User>();
ts.add(user1);
ts.add(user2);
ts.add(user3);
System.out.println(ts);
}
}
package corelesson3;
public class User implements Comparable<User> {
private String name;
private String pass;
private int age;
public User() {
}
public User(String name, String pass, int age) {
this.name = name;
this.pass = pass;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
@Override
public int compareTo(User o) {
if(!this.name.equals(o.name))
//根据年龄字母来排序
{
return this.name.compareTo(o.name);
}else
//如果名字相同 根据年龄来排序
{
return this.age-o.age;
}
//这里内部会自己比较,然后做交换,我们只需要定义谁比较谁就可以了
}
}
4.2 java.util.Comparator
TreeSet 构造的时候使用Comparator 作为 构造函数的参数。
Comparator 是一个接口
package corelesson3;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo2 {
public static void main(String[] args) {
User2 user1 = new User2("mengzi1","24234",23);
User2 user2 = new User2("mengzi2","24234",23);
User2 user3 = new User2("mengzi3","24234",23);
TreeSet<User2> ts = new TreeSet<User2>(new MyComparator());
ts.add(user1);
ts.add(user2);
ts.add(user3);
System.out.println(ts);
// 使用匿名类的方式实现接口Comparator
TreeSet<User2> ts1 = new TreeSet<User2>(new Comparator<User2>() {
@Override
public int compare(User2 o1, User2 o2) {
return o2.getName().compareTo(o1.getName());
}
});
System.out.println("====================");
ts1.add(user1);
ts1.add(user2);
ts1.add(user3);
System.out.println(ts1);
}
//内部类方式实现接口Comparator
private static class MyComparator implements Comparator<User2>{
@Override
public int compare(User2 o1, User2 o2) {
return o1.getName().compareTo(o2.getName());
}
}
}
两个对象 Comparator 比较 返回0 说明是同一个对象 是加不进容器里的
加入第一个,第二个是加不进去的。 添加,删除,遍历 ,和之前是一样的
4.3 数据接结构 底层是TreeMap
5,java.util.Map
- java.util.HashMap(key/vaule)
5.1 基本操作
package corelesson3;
import javax.activation.MailcapCommandMap;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class HashMapDemo1 {
public static void main(String[] args) {
User3 u1 = new User3("001", "mengzi1", 20, "123456");
User3 u2 = new User3("002", "mengzi2", 20, "123456");
User3 u3 = new User3("003", "mengzi3", 20, "123456");
User3 u4 = new User3("004", "mengzi4", 20, "123456");
HashMap<String ,User3> map = new HashMap<>();
map.put(u1.getId(),u1);
map.put(u2.getId(),u2);
map.put(u3.getId(),u3);
map.put(u4.getId(),u4);
//获取map 容器的大小
System.out.println(map.size());
//直接打印它的tostring ,打印全部内容
System.out.println(map);
//获取某个元素 获得001 的信息
System.out.println(map.get("001"));
System.out.println("==================================");
//判断某个key 是否存在
System.out.println(map.containsKey("002"));
// 是否包含某个value
System.out.println(map.containsValue(u2));
// 删除某个元素
User3 rm = map.remove("002");
/**
* 遍历方式1: 把Map中所有的key 都放入set 集合中,然后遍历set 集合
* 然后遍历Set集合, 得到key,通过key 获取value
*/
System.out.println("==========================================");
Set<String> set =map.keySet();
for ( String key:set){
User3 u = map.get(key);
System.out.println(key +"="+u);
}
System.out.println("=============================");
/**
* 遍历方式2
* 放入HashMap集合中的key,value 其实都会被包装成
* Map.Entry这个内部类的属性,
* 有一个键值对就存在一个Map.Entry的实例对象
* 通过entrySet()方法就可以吧这些实例对象都放入Set集合
* 遍历Set 获取每一个对象。
*每一个类型都有getKey 和getValue
*/
Set<Map.Entry<String,User3>> set1=map.entrySet();
for(Map.Entry<String,User3> me:set1){
System.out.println(me.getKey()+"="+me.getValue());
}
}
}
注意: map.put(key,value) 如果key 相同的话 会覆盖前面的value值。
这个特点特别适合用来做计数,
map统计次数
package corelesson3;
import java.util.HashMap;
public class HashMapDemo3 {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("x",10);
map.put("y",20);
//后面x 的值 会覆盖前面的值
map.put("x",30);
//这个特点适合用来做计数
System.out.println("====================================");
String s="asfdasdfsdyasdiobnvuyxzmsdfkshnmoivzvxbbnlajshkjtyierghacb";
/**
* 计算每个字符出现的次数;
* 思路:拿到每个字符,作为key 放入HashMap中,
* 如果map 不中存在,那么put(key,1);
* 如果已经存在 那么put(key,value+1);
*/
HashMap<String, Integer> map1 = new HashMap<>();
//1 遍历
for (int i = 0; i <s.length() ; i++) {
String key=s.substring(i,i+1);
if(!map1.containsKey(key)){
//不存在就放进去
map1.put(key,1);
}else
{
//已经存在就加1
map1.put(key,map1.get(key)+1);
}
}
System.out.println(map1);
}
}
package corelesson3;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TreeSet;
public class HashMapDemo4 {
public static void main(String[] args) {
//要求计数,并且次数按照降序,如果次数相同 ,按照字母升序排序
/**
* 思路: 通过HashMap 集合就可以实现计数操作
* 要排序 可以采用TreeSet 集合
* 并且是根据放入TreeSet 中的对象的属性进行排序的;
* 抽取出一个类来完成这件事
*/
String s="asfdasdfsdyasdiobnvuyxzmsdfkshnmoivzvxbbnlajshkjtyierghacb";
HashMap<String, Integer> map1 = new HashMap<>();
//1 遍历
for (int i = 0; i <s.length() ; i++) {
String key=s.substring(i,i+1);
if(!map1.containsKey(key)){
//不存在就放进去
map1.put(key,1);
}else
{
//已经存在就加1
map1.put(key,map1.get(key)+1);
}
}
//System.out.println(map1);
/**
* 遍历HashMap 集合,然后把每一个key/value 都包装成T对象的实例
* 放入TreeSet 集合,就完成了排序
*/
TreeSet<T> ts = new TreeSet<>();
for(String key:map1.keySet()){
T t = new T(key,map1.get(key));
ts.add(t);
System.out.println();
}
//然后遍历这个集合,或者直接打印
System.out.println(ts);
System.out.println("==================================");
for (T str:ts) {
System.out.println(str);
}
System.out.println("========================");
//迭代器遍历
Iterator<T> itor = ts.iterator();
while (itor.hasNext()){
System.out.println(itor.next());
}
}
}
class T implements Comparable<T>{
private String name;
private int count;
public T() { }
public T(String name, int count) {
this.name = name;
this.count = count;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return name +": "+count +"次";
}
//定义排序的规则
@Override
public int compareTo(T o) {
if(this.count!=o.count){
return o.count-this.count;
}else {
return this.name.compareTo(o.name);
}
}
}
5.2 HashMap 数据结构
底层是一个数组加链表的结构,默认大小是16, 当hash值相同的时候就会放在链表里面,当链表程度大于8 的时候会转化为红黑树,当链表大小小于8 的时候,红黑树会转化为链表。
key/value 放入HashMap 的时候会被包装成Entry 的对象。
b,HashMap 的成员就有Entry的数组,该数组的大小默认是16,永远是2 的次方数
put(key,value) 时 其实就是转化成Entry对象并放入数组中。
c.put方法的实现
1,根据key 做一个hash 算法运算 得到一个hash值。
2,得到hash值去确定 数组的位置。 把hash,和数组长度 传过去。
做的事情就是 hash & (length-1)
等价于 hash % table.length length 是2 的次方数。
正好,获取到了数组的位置 (比如:3 %16 就拿到第三个数组的位置)
3 如果这个位置没有元素,直接包装Entry的实例并给数组赋值,如果计算出的位置已经有元素存在,就要判断是否相同,如果相同,则覆盖,并且要遍历整个链表,如果不覆盖,插入到链表头部。
如果计算出来的位置相同,这就是冲突率,我们要减少冲突率,因为一旦放入链表中,以后总是要遍历链表,效率差,要尽量把元素直接放入数组。而非链表。
所以要根据实际情况 重写hashCode和equals方法
注意2; 底层是数组,尽量减少扩容,所以HashMap 放入元素 也应该估算数组大小,避免扩容操作。
HashMap 中有加载因子 默认:0.75 扩到大2*length
5.3, get 方法实现
通过key 查找元素的算法和放入是一样的。
所以一打放入HashMap就不应该修改和hashCode和equals 方法生成相关属性的值。否则就会找不到。
6 , TreeMap (key/value)
key 必须要能够根据某种规则排序
通过两种比较器,TreeSe用底层就是TreeMap
添加 ,删除,遍历的方式
package corelesson3;
import java.util.Comparator;
import java.util.TreeMap;
public class TreeMapDemo1 {
public static void main(String[] args) {
TreeMap<User, Integer> users = new TreeMap<>();
User user1= new User("menzi1","123123",18);
User user2= new User("lisi","12",21);
User user3= new User("lisi1","12",20);
users.put(user1,1);
users.put(user2,2);
users.put(user3,3);
// 遍历方式 两种 和hashmap 是一样的
for(User key:users.keySet()){
System.out.println(key+"="+users.get(key));
}
System.out.println("=============================");
//定义名字排序规则
TreeMap<User, Integer> users1= new TreeMap<>(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o2.getName().compareTo(o1.getName());
}
});
users1.put(user1,1);
users1.put(user2,2);
users1.put(user3,3);
System.out.println(users1);
}
}
7.HashTable
和HashMap 相比,类的层次结构不一样。当然都实现了Map接口。基本操作都是类似。
package corelesson3;
import java.util.HashMap;
import java.util.Hashtable;
public class HashTableDemo1 {
public static void main(String[] args) {
Hashtable<String, String> hs = new Hashtable<>();
// 添加元素
hs.put("aa","123");
hs.put("bb","456");
hs.put("cc","321");
hs.put("dd","876");
//移除元素
hs.remove("aa");
hs.remove("dd","876");
// 打印对象tostring
System.out.println(hs);
//遍历
for(String key:hs.keySet()){
System.out.println(key+"="+hs.get(key));
}
HashMap<Object, Object> hm = new HashMap<>();
hm.put(null, null);
System.out.println(hm);
// hs.put(null,null); //会抛出 NullPointerException
}
}
7.2 hashMap 可以放 null的值
hashTable 不可以放 null 的值 会抛出NullPointerException 的异常
hashTable 底层源码有很多synchonized 的关键字。
7.3 Hashtable 是线程安全的
线程并发时效率低,但是能保证数据一致性
HashMap 是线程不安全的(线程并发时效率高,数据可能不一致,存在安全问题)
7.4 HashTable还有一个子类 Properties
java.util.Properties 类这个类在将来用的比较多,用来加载资源文件。
java.util.Collections 类
该类封装了一些对集合的操作都是静态方法。
面试有时会问和Collection类有什么关系,其实两个没有任何关系。
Collections 类封装了集合常用的静态方法
Collection 是集合类的上层接口,是 Set接口和List接口的父接口
package corelesson3;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CollectionsDemo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("ee");
list.add("cc");
list.add("dd");
//集合打印
System.out.println(list);
//把里面的数组做倒置
Collections.reverse(list);
System.out.println(list);
System.out.println("=============================");
// 给属数组做一个排序
Collections.sort(list);
System.out.println(list);
System.out.println("=============================");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//做一个倒叙排序,改变比较的o1 和o2 位置即可
return o2.compareTo(o1);
}
});
System.out.println(list);
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(10);
list2.add(20);
list2.add(1000);
list2.add(30);
// 直接获取最大值
System.out.println(Collections.max(list2));
}
}