JAVA--集合

1.1Collection

集合可以理解为数据结构的封装,根据不同的特性及操作性能进行分类

1.2 继承体系

 1.3Collection中常用的方法

public static void main(String[] args) {
		// 创建一个集合对象
		Collection c1 = new ArrayList();
		// 判断是否为空 , 就是个数是否为0
		System.out.println(c1.isEmpty());
		// 已添加元素的个数
		System.out.println(c1.size());
		// 添加
		// int 类型 先自动装箱为integer类型 然后发生多态
		c1.add(1);
		c1.add("a");
		System.out.println(c1.size());
		// 根据元素内容 删除元素
		c1.remove(1);
		System.out.println(c1);
		// 没有提供查询功能和修改功能

		// 遍历
		// 把集合转换为数组
		Object[] arr = c1.toArray();
		// 清空集合
		c1.clear();
		// 判断是否包含某个元素
		System.out.println(c1.contains("a"));
	}

1.4 迭代器

1.4.1概述

迭代器:是一种设计模式,可以使我们进行数据结构操作的时候,可以无需关心其底层实现
提供了一种通用的遍历模式
获取迭代器对象 : Iterator it = 集合对象.iterator();
 有三个方法 :
 hasNext 返回布尔型, 判断是否还有元素,如果true 就说明还有元素
 next 返回Object类型 , 返回下一个元素(当前指向的元素)
 remove 删除指向的元素
迭代器对象一旦创建,那么我们就不能操作集合对象了,尤其是添加和删除操作
如果操作了集合对象,那么需要重新生成迭代器,否则会报错

1.4.2使用

public static void main(String[] args) {
		Collection c1 = new ArrayList();
		c1.add(1);
		c1.add(2);
		c1.add("a");
		c1.add("v");
		c1.add(31);
		System.out.println(c1.size());
		// 创建迭代器对象
		Iterator it = c1.iterator();
		while (it.hasNext()) {
			System.out.println(it.next());
		}
		// 遍历完之后,如果想要再次遍历,必须重新生成迭代器
		// while (it.hasNext()) {
		// System.out.println(it.next());
		// }
		it = c1.iterator();
		// 在循环中想要删除集合中的数据,必须使用迭代器的remove
		while (it.hasNext()) {
			System.out.println(it.next());
			// c1.remove("a");
			it.remove();
		}
		System.out.println(c1);
	}

1.4.3 forEach

增强for循环,foreach 是迭代器的简写方式
 可用于遍历操作,但是不能进行删除,如果要删除,还是要用迭代器

public static void main(String[] args) {
		Collection c1 = new ArrayList();
		c1.add(1);
		c1.add(2);
		c1.add(3);
		c1.add(4);
		// for(数据类型 变量名 : 集合/数组){}
		// 会把集合中的每个元素,依次赋值给变量object
		for (Object object : c1) {
			// c1.remove(1);
			System.out.println(object);
		}
	}

1.5 List

1.5.1 概述

List : 有序 , 可重复
  可重复 : 可以保存重复数据,比如可以添加两个"a"
  有序 : 添加顺序和取出顺序是一致的
  ArrayList : 底层数数组,所以查询修改效率极高
  LinkedList : 底层是双向链表,所以随机添加和删除效率极高
  Vector : 已经过时,ArrayList是它的升级版,vector属于线程安全,效率较低

1.5.2 ArrayList


  ArrayList 底层是 Object[] elementData;  Object数组
  并且可以自动扩容,默认容量是10,扩大容量是1.5倍,非线程安全,效率极高
  如果创建对象后,不添加数据,则容量为0.添加第一个数据的时候,容量为10

public static void main(String[] args) {
		// 创建ArrayList
		List list = new ArrayList();
		// 添加 
		// 尾部添加
		list.add(1);
		list.add("b");
		// 插入到指定下标中
		list.add(0, "a");
		// 删除
		// 根据内容删除
		list.remove("a");
		// 如果传入的是int类型,则为根据下标删除
		list.remove(1);
		// 如果要根据内容删除,但是内容又是整数
		// 就需要转换一下,装箱
		list.remove(Integer.valueOf(1));
		
		list.add("a");
		list.add("b");
		list.add("c");
		// 查询
		System.out.println(list.get(1));
		// 修改
		// 索引 , 要修改的内容
		list.set(1, "bb");
		// 遍历
		System.out.println(list);
		// 迭代器遍历
		for (Object object : list) {
			System.out.println(object);
		}
		// for循环
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
	}

1.5.3 LinkedList

LinkedList 底层是一个双向链表
  链表中 保存的叫节点 , 一个节点中保存三个数据
 1 要添加的数据, 2 上一个节点的对象 , 3 下一个节点的对象
 另外注意链表其实是没有下标的,之所以可以这样访问,是因为人家封装了循环函数
 使我们可以通过下标的形式访问而已,本质还是循环

public static void main(String[] args) {
		// 创建ArrayList
		LinkedList list = new LinkedList();
		// 尾部添加
		list.add(1);
		// 头部添加
		list.push(2);
		// 头部添加
		list.addFirst(3);
		// 尾部添加
		list.addLast(4);
		// 尾部添加
		list.offer(5);
		// 头部添加
		list.offerFirst(6);
		// 尾部添加
		list.offerLast(7);
		// 以上方法虽然挺多,有的返回true 有的没有返回值
		// 本质 就两个方法 linkLast 和 linkFirst

		// ---------- 下面方法和ArrayList一样
		// 添加
		// 尾部添加
		list.add(1);
		list.add("b");
		// 插入到指定下标中
		list.add(0, "a");
		// 删除
		// 根据内容删除
		list.remove("a");
		// 如果传入的是int类型,则为根据下标删除
		list.remove(1);
		// 如果要根据内容删除,但是内容又是整数
		// 就需要转换一下,装箱
		list.remove(Integer.valueOf(1));

		list.add("a");
		list.add("b");
		list.add("c");
		// 查询
		System.out.println(list.get(1));
		// 修改
		// 索引 , 要修改的内容
		list.set(1, "bb");
		// 遍历
		System.out.println(list);
		// 迭代器遍历
		for (Object object : list) {
			System.out.println(object);
		}
		// for循环
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
	}

1.6 Set

1.6.1 特性

set特性 : 无序 不可重复
  不可重复 : 不能添加相同的数据
  无序 : 添加和取出顺序 不保证一致
HashSet : 底层是散列表
TreeSet : 底层是红黑树,元素必须按照特定的规则进行排序
 数字 : 从小到大 , 日期 : 自然日期(昨天,今天,明天) , 字符串 : 每一位的ASCII码进行排序

 1.6.2 TreeSet

public static void main(String[] args) {
		TreeSet set = new TreeSet();
		// 添加
		set.add(1);
		set.add(3);
		set.add(9);
		set.add(2);
		// 删除,根据内容删除
		set.remove(3);
		// 个数
		System.out.println(set.size());
		System.out.println(set);
		// 遍历
		for (Object object : set) {
			System.out.println(object);
		}
	}

1.6.3 HashSet

public static void main(String[] args) {
		HashSet set = new HashSet();
		// 添加
		set.add(1);
		set.add(3);
		set.add(9);
		set.add(2);
		set.add(22);
		set.add(23);
		set.add(12);
		// 删除,根据内容删除
		set.remove(3);
		// 个数
		System.out.println(set.size());
		System.out.println(set);
		// 遍历
		for (Object object : set) {
			System.out.println(object);
		}
	}

1.7 排序

1.7.1 概述

为什么treeSet可以进行自动排序?
 因为要添加的元素,都有比较器,他们都实现了Comparable接口
 并且都实现了 public int compareTo(T o); 方法
如何进行排序呢?
自动调用compareTo方法,根据返回值进行排序
0 说明相等,则不添加 , 返回大于0的,说明要添加的元素比里面的元素大,就会放到后面
返回小于0的,说明要添加的元素比里面的元素小,就放到前面
意味着,要想存储其他元素在treeSet中,那么该元素必须要实现Comparable接口并覆写compareTo方法

1.7.2 Comparable

public class TreeSet1 {
	public static void main(String[] args) {
		TreeSet set = new TreeSet();
		set.add(1);
		set.add(3);
		set.add(2);
		// 因为treeSet会比较,所以必须添加相同类型元素
		// set.add("a");
		System.out.println(set);
		
		set = new TreeSet();
		//  java.lang.ClassCastException: set.User cannot be cast to java.lang.Comparable
		// 因为添加的时候,会调用compareTo方法,所以必须实现comparable接口
		set.add(new User(18));
		set.add(new User(16));
		set.add(new User(11));
		set.add(new User(20));
		System.out.println(set);
	}
}
class User implements Comparable{
	int age;
	public User(int age){
		this.age=age;
	}
	@Override
	public int compareTo(Object o) {
		// this.age 是要添加的元素
		int age1 = this.age;
		// o.age  是集合中的元素
		int age2 = 0;
		if (o instanceof User) {
			User user = (User)o;
			age2 = user.age;
		}
		
		// 0 说明相等,则不添加
		// 返回大于0的,说明要添加的元素比里面的元素大,就会放到后面
		// 返回小于0的,说明要添加的元素比里面的元素小,就放到前面
		return  age2-age1;
	}
	@Override
	public String toString() {
		return age+"";
	}
}

1.7.3 Comparator

比如我们现在的Integer类型来说,已经实现了Comparable接口并覆写了comparTo方法
 而且 是进行升序排序
 假如 我们现在的 需求是降序排序,我们可以去修改Integer的源代码吗? 肯定不行
 comparator 可以解决 , 优先级大于Comparable
 Comparable 是 要添加的数据,实现comparable接口
 comparator 是 当排序不满足我们需求的时候,可以 通过comparator进行扩展
 如果 要添加的元素,是我们写的,例如 刚才的User ,是我们写的,那么我们应该使用comparable来进 行排序
 如果不是不是我们写的类,比如Integer,String等 , 则需要通过 comparator来修改排序规则

public class TreeSet2 {
	public static void main(String[] args) {
		// 把比较器类传入 TreeSet 即可
		// TreeSet set = new TreeSet(new SortInteger());
		
		// 匿名内部类写法
		TreeSet set = new TreeSet(new Comparator() {
			@Override
			public int compare(Object o1, Object o2) {
				// o1 是要添加的元素
				// o2 是集合中的元素
				Integer i1 = (Integer) o1;
				Integer i2 = (Integer) o2;
				// 0 相等
				// 大于0 往后放
				// 小于0 往前放
				return i2 - i1;
			}
		});
		set.add(1);
		set.add(12);
		set.add(3);
		set.add(5);
		set.add(6);
		System.out.println(set);
	}
}

// 比较器类
class SortInteger implements Comparator {
	@Override
	public int compare(Object o1, Object o2) {
		// o1 是要添加的元素
		// o2 是集合中的元素
		Integer i1 = (Integer) o1;
		Integer i2 = (Integer) o2;
		// 0 相等
		// 大于0 往后放
		// 小于0 往前放
		return i2 - i1;
	}

}

1.7.4 Collections

public class ListSort1 {
	public static void main(String[] args) {
		ArrayList li = new ArrayList();
		li.add(1);
		li.add(12);
		li.add(3);
		li.add(7);
		// API排序,本质 还是调用compartTo方法  , 还是Comparable接口
		// 想要使用list的排序,和treeSet是一样的,都需要实现comparable接口
		// 或者 就要使用 comparator 比较器
		Collections.sort(li);
		
		// 修改排序规则 : 通过comparator
		Collections.sort(li, new Comparator() {
			@Override
			public int compare(Object o1, Object o2) {
				Integer i1 = (Integer) o1;
				Integer i2 = (Integer) o2;
				return i2-i1;
			}
		});
		System.out.println(li);
	}
}

1.8 散列表

1.8.1 概述

散列表 : 又叫hash表 , 谐音 哈希表, 数组中保存链表
hash算法 : 是一种安全的加密算法,把不定长数据变成定长数据,不能保证其唯一性
直接寻址法,数字分析法,平方取中法,折叠法,随机数法,除留余数法
hash冲突 : 对同一个对象hash多次,值一定是相同的, 但是对多个对象进行hash,可能生成相同的值

 添加实现原理 :
 1 添加是K-V映射关系 , 比如 姓名 : 张三 , 此时姓名可以是固定的属性,而张三是可变的值
 所以我们一般是通过属性去获取值的,也就意味着通过key获取value
 2 用K调用hashCode()生成hash值,然后对这个值进行hash算法hash,得到对应的下标
 3 判断下标对应的数组中是否有数据,如果没有,就保存这个映射关系(K-V)即可
 4 如果有数据,那么就调用K的equals方法,和数组中对应下标的所有数据进行比较
 5 如果equals进行判断时,有相同的,就不添加此映射关系,新的value值替换原来的value值
 6 如果没有相同的,就把该映射关系,添加到数组对应的链表中的尾部
 意味着,想要使用散列表,我们需要根据需求对hashCode方法和equals方法进行重写
 包装类Integer等 还有String 都覆写了hashCode和equals方法了
 hashMap中 数组长度默认为16 , 默认加载因子是0.75(16*0.75=12) 就是当达到12个的时候,就开始扩容

1.9 Map

1.9.1 HashMap

map key不可重复, value可重复,保存键值对映射关系

public class HashMap1 {
	public static void main(String[] args) {
		HashMap map = new HashMap();
		// 尾部添加
		map.put(new Test(18, "张三"), 21);
		// 存在后,key不添加,value值替换
		map.put(new Test(20, "张三"), 56);
		map.put(new Test(20, "张三2"), 56);
		map.put("a", 56);
		map.put("b", 56);

		// 根据key 删除映射关系
		map.remove(new Test(20, "张三2"));
		// 根据key 查询value
		System.out.println(map.get("a"));

		// 修改 : key不能修改,只能修改value
		// 和添加一样,如果是已有的key则为修改value值
		// 如果是不存在的key则为添加映射关系
		map.put("a", 66);
		// 个数
		System.out.println(map.size());
		// 判断是否为空(个数是否为0)
		map.isEmpty();
		// 是否包含某个key
		map.containsKey("");
		// 是否包含某个value
		map.containsValue("");
		System.out.println(map);
		// 获取map中所有的value,并保存在集合中返回
		Collection values = map.values();
		for (Object object : values) {
			System.out.println(object);
		}
		// 获取map中所有的key,并保存在集合中返回
		Collection keys = map.keySet();
		for (Object object : keys) {
			System.out.println(object + " ==> " + map.get(object));
		}
		// 把map中的key和value封装到entry对象中,并保存在集合中返回
		Set entrys = map.entrySet();
		for (Object object : entrys) {
			Map.Entry entry = (Entry) object;
			System.out.println(entry.getKey()+" --> "+entry.getValue());
		}
	}
}

class Test {
	@Override
	public String toString() {
		return name + ":" + age;
	}

	int age;
	String name;

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Test other = (Test) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	public Test(int age, String name) {
		super();
		this.age = age;
		this.name = name;
	}

}

1.9.2 TreeMap

public class Map_01 {
	public static void main(String[] args) {
		TreeMap map = new TreeMap();
		// 使用TreeMap时,添加的key必须是可以排序的
		// 要么添加的key实现了comparable接口
		// 要么创建TreeMap的时候,传入comparator比较器
		map.put(2, 4);
		// 必须保存的是同类型的数据,因为不同类型没有可比性
		// map.put("a",4);
		map.put(2, 1);
		map.put(12, 21);
		map.put(3, 6);
		System.out.println(map);
		// String 是按照每位的ASCII码进行比较
		map = new TreeMap();
		map.put("a", 1);
		map.put("t", 1);
		map.put("f", 1);
		map.put("b", 1);

		// 注意 {1=1, 15=1, 2=1, 4=1, 6=1, 9=1}
		// 2 > 1999999 因为先比较第一位ASCII码,相同再比较第二位
		// 但是在比较第一位的时候 2 > 1 所以 2 > 19999999
		map = new TreeMap();
		map.put("1", 1);
		map.put("2", 1);
		map.put("9", 1);
		map.put("4", 1);
		map.put("15", 1);
		map.put("6", 1);
		// 面试题
		// 以下程序默认按照ASCII码排序,需求:使他们按照数值大小排序 1 2 4 6 9 15
		map = new TreeMap(new Comparator() {
			@Override
			public int compare(Object o1, Object o2) {
				// o1 是要添加的元素
				// o2 是集合中的元素
				String s1 = (String) o1;
				String s2 = (String) o2;
				// 转换为数值型
				int i1 = Integer.parseInt(s1);
				int i2 = Integer.parseInt(s2);
				return i1 - i2;
			}
		});
		map.put("1", 1);
		map.put("2", 1);
		map.put("9", 1);
		map.put("4", 1);
		map.put("15", 1);
		map.put("6", 1);
		System.out.println(map);
	}
}

1.10 泛型

1.10.1 概述

 泛型 : 在编译过程中进行类型检查,检查类型是否匹配
 如果类型不一致,则无法添加
 某种意义上来讲,泛型统一了集合中元素的类型
 如果不指定泛型,默认为Object类型,什么都能放,因为什么类型都可以转型为Object(多态)
 注意 : 泛型只能写引用类型,不能写基本类型,如果需要保存基本类型,需要些对应的包装类
 int --> Integer 
 优点 : 统一类型,减少强制类型转换,使用更方便
 缺点 : 只能存储单一类型

1.10.2 使用

public static void main(String[] args) {
		ArrayList arr1 = new ArrayList();
		arr1.add(1);
		arr1.add("aa");
		arr1.add(new Object());
		// 遍历时,只能写Object
		for (Object object : arr1) {
			
		}
		ArrayList<String> arr2 = new ArrayList<String>();
		arr2.add("a");
		// 无法添加,只能添加String
		// arr2.add(1);
		// 指定泛型后,遍历时可以写泛型类型,操作更方便,不需要类型转换
		for (String string : arr2) {
			
		}
	}

1.10.3 自定义泛型

 ? : 表示不确定的类型
 T : type 说明是一个java类型
 E : element 元素,一般集合中保存的都是元素,所以一般使用E
 K : key 键,一般在map散列中
 V : value 值,一般在map散列中
 N : number 数字
 一般使用的是大写字母,愿意写什么就写什么,还是要注意望文知义
 如果不传递泛型值,默认为Object
 <T> 就等于 <T extends Object> 可以传入一个任意Object的子类类型,默认是Object
 <T extends Number> 可以传入一个任意的Number的子类类型,不传递默认是Number类型

 

public class Generic {
	public static void main(String[] args) {
		// 没有指定泛型
		MyClass myClass = new MyClass();
		myClass.m1(1);
		myClass.m1("ss");
		myClass.m1(1.5);

		MyClass<Integer> myClass2 = new MyClass<Integer>();
		myClass2.m1(2);
		// myClass2.m1(3.2);
		// myClass2.m1("xx");
		MyClass<Integer> myClass3 = new MyClass();
		// myClass3.m1("");
	}

}

class MyClass<Z> {
	public void m1(Z t) {
		System.out.println(t);
	}
}

1.10.4 练习

/**
 * Map转List并以value值进行排序
 * 
 * map没有办法按照value排序,因为treeMap中的排序规则是按照key排序
 * 
 * 我们没办法修改代码,所以只能按照key排序,所以要想按照value排序,必须转换为list
 * 
 */
public class Test {
	public static void main(String[] args) {
		Map<String, Integer> map = new TreeMap<String, Integer>();
		map.put("a1", 1);
		map.put("a2", 2);
		map.put("a3", 7);
		map.put("a4", 5);
		map.put("a5", 3);
		// System.out.println(map);
		// 因为map中保存键值对映射关系
		// 而list中保存的是单个数据,所以没有办法直接保存KV
		// 因此需要将map中的数据转换为entry对象,然后保存在list中即可
		Set set = map.entrySet();
		// 把set保存到list中
		List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(
				set);
		// 排序
		Collections.sort(list, new Comparator<Entry<String, Integer>>() {

			@Override
			public int compare(Entry<String, Integer> o1,
					Entry<String, Integer> o2) {
				return o1.getValue() - o2.getValue();
			}
		});
		System.out.println(list);
	}
}

  • 41
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值