java集合总结

15 篇文章 0 订阅

一、集合概述
 1.集合分类

  (1)Collection 接口下常用接口有:List 和 Set
                  |--List:有存储顺序 ,可以重复.
                  |--Set:没有存储顺序,不可重复.
     
            List
                  |--ArrayList: 底层为数组结构,查找快,增删慢.
                  |--LinkedList:底层为链表结构,查找慢,增删快.功能上与ArrayList没有区别.
                  |--Vector:不常用,被ArrayList替代,它是线程安全的.而ArrayList是线程不安全的.
   
           Set
                 |--HashSet:只去重复,没有顺序.
                        |--LinkedHashSet:是HashSet的子类,可以去重复,并且保留存储顺序.
                 |--TreeSet:去重复,并且可以按照某种顺序排序.
   Map:键值对,一次存两个对象,键必须唯一,可以根据键查找值.
   Map接下常用类有:HashMap 和  TreeMap
          HashMap: 底层是哈希表数据结构,允许使用null键和null值,该集合不同步,替代Hashtable.
                 |--LinkedHashMap: 去重复的同时保留存储顺序.
          TreeMap: 底层是二叉树数据结构,线程不同步,可以用于给map集合中的键进行排序.
         Hashtable:     底层是哈希表数据结构,不允许使用null键和null值,该集合线程同步.
                 |--Properties: 键和值都是String,用来存储一些配置项.
                实际上Set底层就是使用了Map集合.
  (2)HashSet 是如何保证元素唯一性的?
   它是通过元素的两个方法, hashCode 和  equals 来完成的.
   HashSet的add方法会先调用hashCode方法,如果元素或对象返回的hashCode值相同,才会判断equals是否为true.
   如果hashCode返回值不同,则不会调用equals方法.
  (3)TreeSet 是如何排序的?
   两种方式:自然顺序(Comparable),比较器(Comparator)
    自然顺序:在要放入TreeSet集合的类上实现Comparable接口,并重写compareTo()方法,
             这样对象存入TreeSet的时候就会按照compareTo()方法排序.
    比较器:compareTo()方法只有一个,无法定义两种比较方式,这就需要比较器了.
     在创建TreeSet对象的时候,构造函数可以接受一个Comparator类型的对象,我们可以自定义一个Comparator
     类的子类,并重写compare()方法.
    一旦传入了比较器,TreeSet将不再按照Comparable中的顺序排序,add()方法执行时会按照我们自定义的比较器进行排序.
  (4)HashMap 是如何保证键唯一的?
   存储时:
    和HashSet类似,HashMap存储一个键值的时候,会先调用键(key)对象的hashCode()方法得到哈希值,
    然后在集合中查找是否有哈希值相同的键对象.
    如果没有哈希值相同的键对象,直接将键值对存入.
    如果有哈希值相同的键对象,就逐个和这些键对象进行equals()方法比较,结果为false就将键值对存入,结果为true时,就用新的值(value)覆盖掉原有值.
   获取值时:
    当HashMap在根据指定的键对象获取值的时候,会先对键对象调用hashCode()方法,得到哈希值,然后在集合中查找哈希值相同的对象,逐个equals()方法比较,
    找到equals()结果为true的键值对,将值返回.
  (5)TreeMap如何进行排序的?
   TreeMap在存储键值对的时候,会调用键对象的compareTo()方法和集合中其它的键对象进行比较,根据比较结果以二叉树形式存储,当然和TreeSet类似,如果在创建
   TreeMap的时候在构造函数中传入了比较器,那么存储顺序会以比较器为准.
  (6)泛型
   JDK5之后支持带有泛型的类, 集合上如果加了泛型, 只能存储同一类型的数据, 获取数据时的类型也被指定了.
    这样做避免了一些不安全因素, 而且省去了强转的麻烦
   (7)可变参数
    JDK5之后函数的参数可以定义为可变参数
    定义方式:(类型...变量名)
    可变参数可以接收指定类型的0到多个实参,也可以接收指定类型的一个数组.
    这些实参都会传入到形参变量代表的数组中, 传了几个实参, 形参数组的长度就是几.
   可变参数必须是参数列表的最后一个.
   什么时候用:当我们定义一个函数时,如果需要接受的数据个数不确定时.可用可变参数,这样可以按照实际需求传入不同数量的实参.
 2.List接口介绍

  常用方法:
   (1)boolean add(Object obj)       添加任意类型对象
   (2)Object remove(int index)      删除指定索引上的元素,并且返回这个元素.
   (3)Object set(int index, Object obj) 将集合中指定位置的元素替换为指定对象.
   (4)Object get(int index)    从集合中获取指定索引上的对象.
   各举一例,欲要了解更多可以查API文档.
  遍历:
   (1)普通for循环.
   (2)增强for循环.
   (3)迭代器.
  遍历时删除元素:
   (1)普通for循:由于删除元素后,后面的元素会向前移动,所以每次删除之后需要将循环遍历-1操作(这里需要特别注意).
   (2)增强for循环:无法在循环过程中修改集合.
   (3)迭代器:迭代器在使用的过程中不允许修改集合,如果要删除,必须使用迭代器自身的remove()方法.

		//代码示例:
		//以下代码也适用于LinkedList
		//导包是必须的.
		import java.util.ArrayList;
		import java.util.Iterator;
		
		public class Demo {
		
			public static void main(String[] args) {
				ArrayList<String> list = new ArrayList<String>();
		
				list.add("a");
				list.add("b");
				list.add("c");
				list.add("d");
		
				// 普通for循环
				for (int i = 0; i < list.size(); i++)
					System.out.print(list.get(i));
				System.out.println();
				// 增强for循环
				for (String s : list)
					System.out.print(s);
				System.out.println();
				// 迭代器
				Iterator<String> it = list.iterator();
				while (it.hasNext())
					System.out.print(it.next());
				System.out.println();
				
				/*
				 //为了遍历完就释放迭代器,可以改用for循环
				for(Iterator<String> it = list.iterator();it.hasNext(); )
					System.out.print(it.next());
				 */
			}
		}


 3.List练习
  (1)约瑟夫环问题,即7个小孩围成一个圈,从第一个开始,每数3个出列一个,问最后剩下哪个?
  (2)自定义ArrayList,模拟Java提供的ArrayList,数组实现的列表.

		//问题一
		import java.util.LinkedList;
		public class Demo {
			public static void main(String[] args) {
				LinkedList<String> list = new LinkedList<String>();
				list.add("1");
				list.add("2");
				list.add("3");
				list.add("4");
				list.add("5");
				list.add("6");
				list.add("7");
		
				whileMethod(list);
				// forMethod(list);//两种实现方法,其实都一样.
				System.out.println(list);
			}
			public static void whileMethod(LinkedList<String> list) {
				int index = 0;
				int num = 1;
				while (list.size() > 1) {
					if (num % 3 == 0)
						list.remove(index--);
					index = index == list.size() - 1 ? 0 : index + 1;
					num++;
				}
			}
			public static void forMethod(LinkedList<String> list) {
				for (int index = 0, i = 1; list.size() > 1; i++) {
					if (i % 3 == 0)
						list.remove(index--);
					index = index == list.size() - 1 ? 0 : index + 1;
				}
			}
		}
		
		//问题二
		public class MyArrayList<E> { // <E>为泛型

			private Object[] arr = new Object[10]; // 用来装载数据的容器
			private int size; // 集合的大小
			
			 // 添加一个对象到ArrayList中, 内部就是将对象添加到数组中
			public boolean add(E e) {
				if (size == arr.length) { // 如果数组装满了
					Object[] newArr = new Object[arr.length * 3 / 2 + 1]; // 创建一个更大的新数组
					System.arraycopy(arr, 0, newArr, 0, size); // 将原有数据拷贝到新数组中
					arr = newArr; // 成员变量记住新数组
				}
				arr[size++] = e;
				return true;
			}
		
			 //删除指定索引上的元素
			public E remove(int index) {
				checkRange(index);
				Object oldElement = arr[index]; // 记住要删除位置上的元素
				int length = size - index - 1; // 要删除的元素后面有几个元素
				if (length > 0) // 如果要删除的位置后面有元素
					System.arraycopy(arr, index + 1, arr, index, length); //见 ① 
				arr[--size] = null; // 将最后一个元素删除
				return (E) oldElement; // 返回被删除的元素(在数组中被覆盖了)
			}
			//① 将要删除位置后面的元素向前移动一位(覆盖了要删除的位置)
			
			 //将集合中指定位置上的元素替换为指定元素
			public E set(int index, E e) {
				checkRange(index);
				Object oldElement = arr[index]; // 记住原对象
				arr[index] = e; // 将原对象替换为新对象
				return (E) oldElement; // 返回原对象
			}
		
			// 检查索引是否越界
			private void checkRange(int index) {
				if (index >= size)
					throw new IndexOutOfBoundsException("Index: " + index + ", Size: "
							+ size);
			}
		
			//获取集合中指定索引上的元素
			public E get(int index) {
				checkRange(index);
				return (E) arr[index];
			}
		
			 // 获取集合的大小
			public int size() {
				return size;
			}
		
			 //重写Object类的toString, 返回集合中每个元素的toString
			public String toString() {
				StringBuilder sb = new StringBuilder();
				sb.append("[");
				for (int i = 0; i < size; i++)
					// 将集合中每个元素添加到StringBuilder中
					sb.append(arr[i] + (i < size - 1 ? ", " : ""));
				sb.append("]");
				return sb.toString();
			}
		}		
	

 4.Set接口介绍
  常用方法:
   由于它和List都实现了Collection接口,所以Collection接口中的方法他们都能用,具体请参考API文档.
   由于它没有索引,所以有关索引的方法它都不能用.
  遍历:
   由于没有索引所以只有两种方式来遍历
   迭代器
   高级for循环(实际也是用迭代器实现的,只要迭代器能用,它就能用)
  代码不再演示.
 5.Set练习
  从键盘接收3个学生的考试成绩, 对其按照总分排序, 输出到屏幕.
   考试成绩输入格式:
    张三,80,85,80
    李四,70,70,80
    王五,90,90,90
   屏幕输出格式:
    王五,90,90,90,270 
    张三,80,85,80,245
    李四,70,70,80,220

		//代码实现
		import java.io.BufferedReader;
		import java.io.IOException;
		import java.io.InputStreamReader;
		import java.util.TreeSet;
		
		public class Demo {
			public static void main(String[] args) throws IOException {
				System.out.println("请输入学生成绩:");
				BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
				TreeSet<Student> ts = new TreeSet<Student>();
				for (int i = 0; i < 3; i++) {
					String line = br.readLine();
					String[] arr = line.split(",");//注意这里逗号为中文逗号,填写时就用中文逗号.
					Student s = new Student(arr[0], Integer.parseInt(arr[1]),
							Integer.parseInt(arr[2]), Integer.parseInt(arr[3]));
					ts.add(s);
				}
				System.out.println("考试成绩排序后的结果为:");
				for (Student s : ts)
					System.out.println(s);
			}
		}
		
		class Student implements Comparable<Student> {
			private String name;
			private int chinese;
			private int math;
			private int english;
			private int sum;
		
			public Student(String name, int chinese, int math, int english) {
				super();
				this.name = name;
				this.chinese = chinese;
				this.math = math;
				this.english = english;
				this.sum = chinese + math + english;
			}
		
			public String toString() {
				return name + "," + chinese + "," + math + "," + english + "," + sum;
			}
		
			@Override
			public int compareTo(Student o) {
				int i = o.sum - this.sum; // 重写compareTo方法,按总分排序.
				return i != 0 ? i : 1;
			}
		}

 6.Map介绍
  Map集合常用的方法
   (1)V put(K key, V value)
     存储一个键值对,如果键在集合中存在,值将会覆盖原有值,并返回原有值,原有值不存在则返回null.
     当然,如果支持nul键值对的HashMap来说,如果原有值存的就是null,则返回的也是null.
   (2)V get(Object key)
     根据键获取值,如果不存在,返回null.
   (3)V remove(Object key)
     根据键删除值,并将值返回,如果不存在,返回null.
   (4)boolean containsKey(Object key)
     判断指定的键对象是否存在.
  Map集合的遍历
   (1)keySet: Map集合的keySet()方法可以得到一个所有键对象组成的Set集合,遍历这个Set集合可以得到每一个键对象
      再根据键对象即可获取值.
   (2)entrySet: Map集合的entrySet()方法可以得到一个所有Entry对象(键值对)组成的Set集合,遍历这个Set集合
        可以得到每一个Entry对象,再使用Entry的getKey()方法和getValue()方法获取键和值.

		//代码示例
		import java.util.HashMap;
		import java.util.Map.Entry;
		import java.util.Set;
		
		public class MapIterator {
			public static void main(String[] args) {
				HashMap<String, String> h = new HashMap<String, String>();
				h.put("张三", "10");
				h.put("李四", "20");
				h.put("王五", "30");
				h.put("赵六", "40");
				//遍历方式一
				Set<String> s = h.keySet();//返回键的Set视图
				for (String key : s)
					System.out.println(key + ": " + h.get(key));
				//遍历方式二
				Set<Entry<String, String>> set = h.entrySet();// 封装成Entry对象然后装入Set集合
				for (Entry<String, String> e : set)
					System.out.println(e.getKey() + ": " + e.getValue());
			}
		}
	


 7.练习
  定义一个函数,函数中接收一个String,统计每个字符出现的次数
   (1)不指定顺序打印
   (2)按字母顺序打印  
   (3) 按存储顺序打印
   (4) 按出现次数打印 

import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

public class HomeWork {

	public static void main(String[] args) {
		countTimes1("bbcddddaaa");//没有顺序: {d=4, b=2, c=1, a=3}
		countTimes2("bbcddddaaa");//字母顺序: {a=3, b=2, c=1, d=4}
		countTimes3("bbcddddaaa");//出现顺序: {b=2, c=1, d=4, a=3}
		countTimes4("bbcddddaaa");// 按出现的次数排序 {c=1, b=2, a=3, d=4}
		countTimes5("bbcddddaaa");// 按出现的次数排序 {c=1, b=2, a=3, d=4}
	}
	// (1)
	public static void countTimes1(String s) {
		HashMap<Character, Integer> hm = new HashMap<Character, Integer>();
		char[] arr = s.toCharArray();
		for (Character c : arr)
			if (hm.containsKey(c))
				hm.put(c, hm.get(c) + 1);
			else
				hm.put(c, 1);
		System.out.println(hm);
	}
	// (2)
	public static void countTimes2(String s) {
		TreeMap<Character, Integer> hm = new TreeMap<Character, Integer>();
		char[] arr = s.toCharArray();
		for (Character c : arr)
			if (hm.containsKey(c))
				hm.put(c, hm.get(c) + 1);
			else
				hm.put(c, 1);
		System.out.println(hm);
	}
	// (3)
	public static void countTimes3(String s) {
		LinkedHashMap<Character, Integer> hm = new LinkedHashMap<Character, Integer>();
		char[] arr = s.toCharArray();
		for (Character c : arr)
			if (hm.containsKey(c))
				hm.put(c, hm.get(c) + 1);
			else
				hm.put(c, 1);
		System.out.println(hm);
	}
	// (4)很烂的方法
	public static void countTimes4(String s) {
		HashMap<Character, Integer> hm = new HashMap<Character, Integer>();
		LinkedHashMap<Character, Integer> lhm = new LinkedHashMap<Character, Integer>();// 定义保留存储顺序的map

		char[] arr = s.toCharArray(); // 将字符串转为字符数组
		for (Character c : arr)
			// 循环遍历每个字符
			if (hm.containsKey(c)) // 判断Map中是否包含这个字符
				hm.put(c, hm.get(c) + 1); // 如果包含, 将以前的次数获取出来, 加1, 再覆盖
			else
				hm.put(c, 1); // 如果不包含, 将字符存入, 次数存1
		int[] a = new int[hm.size()]; // 定义一个数组用来存储values(次数)
		int i = 0;
		for (Entry<Character, Integer> entry : hm.entrySet()) { // 遍历hm
			a[i++] = entry.getValue(); // 将values(次数)存入数组
		}
		Arrays.sort(a); // 对数组进行排序
		for (int j = 0; j < a.length; j++)
			// 遍历数组
			for (Entry<Character, Integer> entry : hm.entrySet())
				// 遍历hm
				if (entry.getValue() == a[j]) // 查找hm中value(次数)和数组中相等的
					lhm.put(entry.getKey(), a[j]); // 如果相等,则把对应的Key存入保留存储顺序的lhm中
		System.out.println(lhm);
	}
	// (4)第二种方法,这种比较好一些
	public static void countTimes5(String s) {
		Map<Character, Integer> map = new HashMap<Character, Integer>();
		for (char c : s.toCharArray())
			map.put(c, map.containsKey(c) ? map.get(c) + 1 : 1);

		// 以上的操作统计出了每个字符出现的次数, 但是没有顺序, 只要将这些结果排序即可

		Set<Entry<Character, Integer>> entrySet = map.entrySet(); // 获取出map集合中的所有Entry
		TreeSet<Entry<Character, Integer>> treeSet = new TreeSet<Entry<Character, Integer>>(
				new EntryComparator()); // 创建TreeSet用来排序
		treeSet.addAll(entrySet); // 将entrySet中的键值对都装入treeSet, 排序
		System.out.println(treeSet); // 打印结果
	}
}
class EntryComparator implements Comparator<Entry<Character, Integer>> {
	public int compare(Entry<Character, Integer> o1,
			Entry<Character, Integer> o2) {
		int gap = o1.getValue() - o2.getValue(); // 值的比较结果
		return gap != 0 ? gap : o1.getKey() - o2.getKey(); // 如果值不一样, 按照值排序,值一样的话按照键排序
	}
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值