首页 > 其他 > 详细

常用类,集合 一

时间:2021-04-12 15:17:51      阅读:19      评论:0      收藏:0      [点我收藏+]

1. 常用类

1.1 String

String是java.lang 包下的不需要导包

1.1.1 字符串特点

  • 所有双引号字符串,都是String类的对象
  • 创建后不可变
  • 值虽然不可变,但是可以共享
  • 字符串效果相当于字符数组(char[]),但是底层原理是字节数组{byte[]).JKD8及以前是字符数组,,9开始是字节数组

1.1.2 字符串创建

技术分享图片
通过new 创建的字符串对象,每次new都会申请一个内存空间,虽然内容相同但是地址不同。
通过""创建的字符串,JVM只会建立一个String对象,并在字符串常量池中维护
例:
技术分享图片

1.1.3 字符串的比较

  • 使用==比较
    • 基本类型:比较数值
    • 引用类型:比较地址值
  • 字符串比较内容使用equals()
    字符串遍历用charAt(index)

1.2 StringBuilder

技术分享图片

常规字符串拼接操作,每次拼接都会构建一个新的String对象,耗时且浪费内存空间。所以引入StringBuilder解决这个问题
String:内容不可变
StringBuilder 可看作一个容器,对象中的内容是可变的

1.2.1 StringBuilder该方法

  • 构造方法
    StringBuilder()
    StringBuilder(String str)
  • 添加和反转方法
    append(任意类型),返回StringBuilder对象本身,不会创建新的
    reverse() 返回相反序列
    扩展 链式编程, 方法返回值是对象,可以一行内继续调用该对象的方法
    sb.append("hello").append("world").append("100");

1.2.2 StringBuilder和String相互转换

  • StringBuilder 转换为String使用toString方法
  • String 转换为StringBuilder使用StringBuilder(string)构造方法

1.3 Arrays

Arrays 工具类,包含操作数组的各种方法
工具类,构造方法用private修饰(API里没有构造方法).成员用public static修饰

  • Arrays常用方法
    • toString(int[] a) 数组内容转换为字符串
    • sort(int[]a) 按照数字顺序排列指定的数组

1.4 包装类

封装基本数据类型,提供更多功能操作该数据,如基本数据类型和字符串之间的转换
除Integer--int; Character--char外,其他类型的包装类,都对应首字母大写即可

1.4.1 Integer 常用方法

  • 构造方法
    Integer(int value) 过时
    Integer(String s) 过时
    public static Integer valueOf(int i) 返回指定int值的Integer实例
    public static Integer valueOf(String s) 返回一个保存String值的Integer对象,字符串需要是数字字符串,否则NumberFormatException
    推荐使用静态方法来得到Integer对象
  • int转化为String
    推荐String类里的方法public static String valueOf(int i) 返回int参数对应的字符串。
  • String转化为int
    推荐Integer里的方法 public static int parseInt(String s) 将字符串解析为int类型。(最终转化成的是啥,就在相应的类中)
    例1. 字符串和int转换
package collection;

public class IntegerDemo {
    public static void main(String[] args) {
        //int---String
        int number = 100;
        //方式1,字符串拼接
        String s1 = ""+number;
        System.out.println(s1);

        //方式2.
        //public static String valueOf(int i) 返回int参数对应的字符串
        String s2 = String.valueOf(100);
        System.out.println(s2);
        System.out.println("=================");

        //String--- int
        //方式1 String---Integer--int
        String s = "100";
        Integer i = Integer.valueOf(s);
        int i1 = i.intValue();
        System.out.println(i1);

        //方式2  String---int
        // parseInt(String s)
        int i2 = Integer.parseInt(s);
        System.out.println(i2);
    }
}

例2
技术分享图片

package collection;

import java.util.Arrays;

/*
需求:
    有一个字符串"91 27 46 38 50",请写程序最终输出“27 38 46 50 91”
思路:
    1.定义一个字符串
    2. 把字符串中的数字存储到一个int类型的数组中
        split 分割成String[]数组
        parseInt 转化字符串为int型数组
    3. 对int型数组排序
    4. 排序后数组使用StringBuilder来拼接成字符串
    5. 输出结果
 */
public class IntegerTest {
    public static void main(String[] args) {
        String s ="91 27 46 38 50";
        String[] strArray = s.split(" ");
/*        for (int i = 0; i < strArray.length; i++) {
            System.out.println(strArray[i]);
        }*/

        int[] arr = new int[strArray.length];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = Integer.parseInt(strArray[i]);
        }
/*        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }*/
        Arrays.sort(arr);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            if(i == arr.length-1){
                sb.append(arr[i]);
            }else{
                sb.append(arr[i]).append(" ");
            }
        }
        String s1 = sb.toString();
        System.out.println("result:"+ s1);
    }
}

1.4.2 自动装箱和拆箱

  • 装箱 基本数据--》包装类
  • 拆箱 包装类--》基本数据
    手动装箱 Integer i = Integer.valueOf(100)
    自动装箱 Integer ii = 100;
    手动拆箱 ii = ii.intValue()+200;
    自动拆箱 ii+=200; ii+200 隐含拆箱调用intValue方法的动作
  Integer iii = null;
  if(iii!=null){
    iii +=300; //NullPointException
  }

在开发中使用引用变量/对象之前(含包装类对象),最好先做不为null的判断

1.5 日期类

1.5.1 Date 类

Java.util.Date表示一个特定的时间,精确到毫秒

  • 构造方法
    Date()
    Date(long date) date表示从标准基准时间以后的指定毫秒数,即1970年1月1日00:00:00 GMT。
  • 常用方法
    getTime() 自1970年1月1日以来,由此Date对象表示的00:00:00 GMT的毫秒数
    setTime(long time) 设置时间
    d.getTime()*1.0/1000/60/60/24/365 年
    扩展CST指中国标准时间

1.5.2 SimpleDateFormat类

用于格式化日期和解析日期

  1. 日期的格式由日期和时间模式字符串指定
  • y 年
  • M 月
  • d 日
  • H 时
  • m 分
  • s 秒
  1. 常用方法:
  • 构造方法
    SimpleDateFormat() 默认斜杠格式
    SimpleDateFormat(String pattern) 指定格式
  • 格式化 Date--> String
    String format(Date date) 将日期格式成日期/时间字符串
  • 解析 String --> Date
    Date parse(String source) 给定字符串生成日期
    Date d = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
    sdf.format(d)
    String ss = "2048-08-09 11:11:11";
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date dd = sdf2.parse(ss);

1.5.3 Calendar类

提供某一时刻和日历字段之间转换方法

常用方法:

  • 类方法getInstance用于获取Calendar对象,其日历字段已使用当前日期和时间初始化。
    虽然Calendar是抽象类,但是getInstance通过其子类或者孙类创建了实例。
  • get(int field)指定字段值
  • void add (int field, int amount)给指定字段,添加或者减去值
  • void set(int year, int month, int date)设置当前日历的年月日
		Calendar c = Calendar.getInstance();
		int year = c.get(Calendar.YEAR);
		int month = c.get(Calendar.MONTH)+1;
		int day = c.get(Calendar.DATE);
		System.out.println(year + " year " + month+" month "+ day+" day");
		
		
		c.add(Calendar.YEAR, 10);
		c.add(Calendar.DATE, -5);
		year = c.get(Calendar.YEAR);
		month = c.get(Calendar.MONTH)+1;
		day = c.get(Calendar.DATE);
		System.out.println(year + " year " + month+" month "+ day+" day");
		
		c.set(2048, 11, 11);
		c.add(Calendar.DATE, -5);
		year = c.get(Calendar.YEAR);
		month = c.get(Calendar.MONTH)+1;
		day = c.get(Calendar.DATE); 
		System.out.println(year + " year " + month+" month "+ day+" day");

结果:
2021 year 4 month 8 day
2031 year 4 month 3 day
2048 year 12 month 6 day
例 输出2月份有多少天(借助Calendar不用手动判断是否闰年)

/*
需求:获取任意一年的二月有多少天
思路:
3月减一天就是2月的最后一天,也就输出了二月有多少天
1.键盘录入任意月份
2. 设置日历对象的年月日
    年:来自键盘录入
    月:设置为2即为3月
    日:设置为1
3. 往前推一天,并输出
 */
public class CalendarDemo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int year = sc.nextInt();
        int month = 2;
        int day =1;

        Calendar calendar = Calendar.getInstance();
        calendar.set(year,month,day);
        calendar.add(Calendar.DATE,-1);

        System.out.println(calendar.get(Calendar.DATE));


    }

1.6 System

包含静态字段和方法,不能被实例化
常用方法

  • void exit(int status) 终止当前运行的java虚拟机,非零表示异常终止
  • currentTimeMills()返回当前时间距离1970年1月1日的毫秒数

1.7 Math

基本数值类型工具类
常用方法

  • abs(int a)绝对值
  • ceil(double a) 大于等于a的整数 double形式,如13.0
  • floor(double a) 小于等于a的整数double形式
  • round(float a) 四舍五入
  • max(int a, int b) 取最大值
  • min(int a, int b) 取最小值
  • pow(double a, double b) 返回a的b次幂
  • random() 返回0.0到1.0之间的随机数
    例 求1到100之间的随机数 (int)(Math.random()*100+1) 0到99要加1,同时int强转为整数

2 集合

存储多个数据,且长度不固定,原有的数组不能满足需求,引入集合的概念
集合特点:可变存储空间,容量可以发生改变

2.1 集合结构

技术分享图片

2.2 ArryList

ArrayList:

  • 底层是可调整大小的数组实现
  • 泛型,表示是数据类型,在使用的地方用具体的引用数据类型替换

2.1.1 ArrayList常用方法

  • ArrayList() 创建一个空的集合对象
  • add(E e)追加指定的元素
  • add(int index, E e) 指定位置插入指定的元素,索引超出范围( index < 0 || index > size() )报数组越界Exception
  • get(int index) 返回列表中指定位置元素
  • remove(int index) 删除指定位置元素;remove(Object o) 删除第一个遇到的指定元素
  • set(int index, E element) 替换指定位置的元素
  • size() 返回元素数
    扩展: 为了提高代码的可复用性,采用方法改进。明确两点,返回值类型,及参数类型。
    如果传递的参数是引用类型,那么引用类型在方法里值改变了,在外面也会改变

2.3 Collection

2.3.1 Collection集合概述

  • 单列集合的顶层接口
  • JDK只提供此接口的子接口如List和Set,不提供其直接实现
    通过子类(多态方式)创建Collection集合的对象
    Collection c = new ArrayList();

2.3.2 Collection集合常用方法

  • boolean add(E e) 添加元素
  • boolean remove(Object o) 删除
  • void clear() 清空
  • boolean contains(Object o) 判断集合中是否存在指定的元素
  • boolean isEmpty()判断集合是否为空
  • int size() 集合长度,元素个数

2.3.3 Collection集合遍历

Iterator:迭代器,集合专用遍历方式

  • Iterator iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
  • 迭代器是通过集合中iterator()返回得到的,所以说迭代器依赖于集合而存在的
    Iterator中常用的方法
  • E next()返回迭代中的下一个元素
  • boolean hasNext():迭代还有更多元素,返回true
    public static void main(String[] args) {
    //创建集合对像
    Collection<String> c = new ArrayList<String>();
    //创建元素,有时元素/对象不是简单的字符串,需要显示创建步骤
    String s1 = "hello";
    String s2 = "world";
    String s3 = "java";
    //添加元素到集合
    c.add(s1);
    c.add(s2);
    c.add(s3);

    //遍历集合
    //获取迭代器
    Iterator <String> iterator = c.iterator();
    //通过迭代器判断是否为空,空还去使用会报NoSuchElementException,所以要提前判断
    while(iterator.hasNext()){
        //获取下一个元素
        String s = iterator.next();
        System.out.println(s);
    }

    }

2.4 List集合

2.4.1 List集合特点:

  • 有序集合(序列)--存储和取出的元素顺序一致。可以精确控制列表中的每个元素的插入位置,通过索引可以访问元素
  • 可重复 -- 不同于Set集合,列表是允许元素重复的

2.4.2 List集合特有方法(collection中没有的)

在子类ArrayList学过,注意操作时要保证索引是存在的
add(int index,E Element),E remove(int index), E set(int index, E element), E get (int index)
例:遍历List集合可以用迭代器,因为其是序列还可用for循环遍历,判断条件是size的大小

for(int i =0; i<list.size();i++){
	String s = list.get(i);
	System.out.println(s);
}

2.4.3 List并发修改异常ConcurrentModificationException

  • 使用迭代器一遍遍历,一边往集合里添加元素代码
package collections;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/*
 * 需求:遍历集合,如果集合中有world元素,就添加一个"javaee" 元素
 */
public class ListDemo {

	public static void main(String[] args) {

		List<String> list = new ArrayList<String>();
		list.add("hello");
		list.add("world");
		list.add("happy");
		
		Iterator<String> it = list.iterator();
		boolean flag = false;
		while(it.hasNext()){
			String s = it.next();
			if("world".equals(s)){
				list.add("javaee");
			}
			
		}
		System.out.println(list);
	}

}
  • 输出结果抛出并发修改异常
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
	at java.util.ArrayList$Itr.next(ArrayList.java:791)
	at collections.ListDemo.main(ListDemo.java:19)

并发修改异常ConcurrentModificationException 继承RuntimeException,是运行期异常

  • 源码分析
    将涉及到的接口和主要方法摘抄到analysis.txt里,逐个分析。(熟练后不用摘出来)
public interface List<E>{
	 Iterator<E> iterator();
	 boolean add(E e);
}
public abstract class AbstractList<E> {
  // 4. 跟中modCount,发现在父类里定义初始值为0
  protected transient int modCount = 0;
 
}
public class ArrayList<E> extends AbstractList<E> implements List<E>{
        //3. 跟踪发现创建迭代器时,实例化了内部类Itr
	public Iterator<E> iterator() {
        return new Itr();
        }
      
private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such

        //2. 跟踪expectedModCount在初始化Itr的时候是和modCount相等的
	// modCount -- actual modified times
	// expectedModCount -- expect modified times
        int expectedModCount = modCount;
		
        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        // 1. 异常抛出部位,原因modCount和预期的不符
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
    
   public boolean add(E e) {
        //5. 跟踪add方法 发现每次add时modCount+1
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
}
  • 解决方法,通过for循环遍历
for(int i =0; i< list.size();i++){
	String s = list.get(i);
	if("world".equals(s)){
  	  list.add("javaee");
	}		
}
  • 结论:ArrayList迭代器遍历的时候,不能同时改变元素长度,否则抛出ConcurrentModificationException异常
    产生原因:迭代器遍历过程中,修改集合中元素的长度,造成迭代器获取元素中判断预期修改值和实际修改值不一致
    解决方案: 用for循环遍历,然后用集合对象做对应的操作即可

2.4.4 ListIterator列表迭代器

  • List集合特有的迭代器,可以通过listIterator()方法得到

  • 该迭代器允许沿任一方向遍历列表,允许在迭代期间修改列表,并获取列表中迭代器的当前位置

  • 常用方法

    • E next()
    • hasNext()
    • previous() 不常用,需要先把集合遍历到最后,再往之前遍历
    • hasPrevious()
    • add() 通过列表迭代器来添加,而不是集合的添加方法
  • 为什么ListIterator没有并发问题
    在ArrayList的内部类ListItr中add方法如下,最后会把增加的modCount赋值给expectedModCount.
    二者始终相等,所以next不会报错

       public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                ArrayList.this.add(i, e);
                cursor = i + 1;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

2.4.5 增强for循环

  • 简化数组和Collection集合的遍历
    • 实现Iterable接口的类,其对象可以使用增强for循环(Map并没有实现Iterable接口,不能直接使用增强for来遍历它)
    • JDK5之后出现的,遍历集合内部原理是一个Iterator迭代器
  • 增强for循环称为for-each格式:
    for(元素数据类型 变量名: 数组或者Collection集合){
    //此处使用变量即可,该变量就是元素
    }
    只需要遍历查看推荐使用增强for,如果想要使用List里的索引操作,要使用普通for
    例:
package collection;

import java.util.ArrayList;
import java.util.List;

public class ForDemo {
    public static void main(String[] args) {
        int [] arr = {1,2,3,4,5};
        for(int a: arr){
            System.out.println(a);
        }
        List<String> list = new ArrayList<String>();
        list.add("hello");
        list.add("world");
        list.add("java");
        for(String s : list){
            System.out.println(s);
            if(s.equals("world")){
                list.add("javaee");
            }
        }

    }
}

输出结果,抛出并发修改异常:

1
2
3
4
5
hello
world
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)
	at collection.ForDemo.main(ForDemo.java:16)

Process finished with exit code 1

2.4.6 常见数据结构

  • 栈 后进先出
  • 队列 先进先出
  • 数组 查询快,增删慢的模型
    查询数据,索引定位,查询效率高
    删除数据,原始数据删除,后面每个数据前移,删除效率低
    添加数据,添加位置后每个数据后移,再添加元素,添加效率低
  • 链表
    技术分享图片
  • 对比数组,链表是一种增删快,查询慢的模型
    增删只需要改变地址的指向,查询元素是否存在或者查询第N个元素必须从head节点开始查

2.4.7 List集合子类

  1. List集合常用子类:ArrayList,LinkedList
  • ArrayList:底层数据结构是数组,查询快,增删慢
  • LinkedList:底层是链表,查询慢,增删快
  • ArrayList和LinkedList继承List,所以也有三种遍历方式,迭代器遍历,普通for,增强for遍历
        LinkedList<String> linkedList = new LinkedList<String>();
        linkedList.add("最难");
        linkedList.add("不过是");
        linkedList.add("坚持");
        for (String s : linkedList){
            System.out.println(s);
        }
  1. LinkedList集合特有方法
  • void addFirst(E e) 在该列表开头插入指定的元素
  • void addLast(E e) 插入元素到末尾
  • E getFirst() 返回第一个元素
  • E getLast() 返回最后一个元素
  • E removeFirst() 删除并返回第一个元素
  • E removeLast() 删除并返回最后一个元素

2.5 Set

2.5.1 Set接口

Set是个接口,继承Collection, 特点

  • 不包含重复元素
  • 没有带索引的方法,所以不能使用普通for循环遍历
    HashSet 底层是哈希表,对集合的迭代顺序不做任何保证
//HashSet 底层是哈希表,对集合的迭代顺序不做任何保证
public class SetDemo {
    public static void main(String[] args) {
        Set<String> set = new HashSet<String>();

        set.add("hello");
        set.add("world");
        set.add("java");
        //不包含重复元素,已经有hello了,不能再次添加进去
        set.add("hello");

        //遍历
        for(String s: set){
            System.out.println(s);
        }
    }
}

输出:
world
java
hello

2.5.2 哈希值

哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中方法 public int hashCode() 返回对象的哈希值
默认情况下,不同对象的hashcode是不同的
hashcode()方法被重写时,可以实现不同对象的哈希值相同。
System.out.println("重地".hashCode()); //1179395
System.out.println("通话".hashCode()); //1179395
String类对hashcode方法进行了重写,所以上述两个汉字字符输出的hashCode一样的
equals比较的是hashCode 和 == 是不一样的。 ==比较的是内存地址, 而hashCode是根据实例变量计算出来的。

2.5.3 HashSet

  • 特点:
    • 底层数据结构是哈希表
    • 对集合的迭代顺序不做任何保证,不保证存储和取出元素顺序一致
    • 没有带索引的方法,不能普通for循环遍历
    • Set集合,不包含重复元素的集合
  • HashSet集合保证元素唯一性源码分析
Set<String> set = new HashSet<String>();

set.add("hello");
set.add("world");
set.add("java");
------------------------------------------

public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
//hash值和元素的hashCode()方法相关
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;

   //哈希表未初始化,就对其进行初始化
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
   //根据对象的哈希值计算出对象的存储位置,如果该位置没有元素就存储元素
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        /*
            存入的元素和以前的元素比较哈希值
            如果哈希值不同,会继续向下比较,把元素添加到集合
            如果哈希值相同,会比较key是否==,或者调用对象的equals()方法比较
                如果返回false,会继续向下执行,把元素添加到集合
                如果返回的是true,说明元素重复,不存储
        */
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

HashSet集合添加一个元素的过程
技术分享图片
HashSet要保证元素的唯一性,需要重写hashCode()和equals()(equal保证内容)方法

2.5.4 常见数据结构之哈希表

  • JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
  • JDK8之后,在长度比较长的时候,底层实现了优化
    技术分享图片
    例 hashSet 添加并遍历Student对象
package collection;

import java.util.HashSet;

/*
 需求:
    创建一个存储对象的集合,存储3个学生对象。使用程序实现控制台遍历该集合
 思路:
    1.定义学生类
    2.创建HashSet集合对象
    3.创建学生对象
    4.把学生添加到集合
    5.遍历集合(增强for)
    6.重写hashCode和equals方法,Idea自动生成就行
 */
public class HashSetDemo1 {
    public static void main(String[] args) {
        HashSet<Student> hs = new HashSet<Student>();
        Student s1 = new Student("严宽","35");
        Student s2 = new Student("陈晓","30");
        Student s3 = new Student("何家劲","52");
        Student s4 = new Student("何家劲","52");

        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        //重写hashCode和equals方法,不重写就可能添加重复对象
        hs.add(s4);
        for(Student s : hs){
            System.out.println(s);
        }
    }
}
/////////////////////////////////////////////////////////
package collection;

import java.util.Objects;

public class Student {
    private String name;
    private  String age;

    public Student(String name, String age) {
        this.name = name;
        this.age = age;
    }

    public Student() {
    }

    public String getName() {
        return name;
    }

    public String getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(String age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name=‘" + name + ‘\‘‘ +
                ", age=‘" + age + ‘\‘‘ +
                ‘}‘;
    }
    
    @Override
    public boolean equals(Object o) {
        //引用地址一样,返回true                
        if (this == o) return true;
        //不是同一个类对象,返回false
        if (!(o instanceof Student)) return false;
        Student student = (Student) o;
        //比较两个字段,只有两个字段都相同才返回true
        return Objects.equals(getName(), student.getName()) && Objects.equals(getAge(), student.getAge());
    }

    //hashcode将字段传入Object的hash方法来计算    
    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }
}

2.5.5 LinkedHashSet

  • 特点
    • 哈希表和链表实现的Set接口,具有可预测的迭代次序
    • 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
    • 由哈希表保证元素唯一,集合中元素没有重复的
      (=哈希表是数组+链表,此处又有个链表,底层结构是什么样的???以后深入学习补充。。。。=)

2.5.6 TreeSet集合

  • 特点
    • 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
      TreeSet()默认构造器,根据元素的自然排序进行排序
      TreeSet(Comparator comparator), 根据指定的比较器进行排序,插入到排序集中的所有元素必须实现Comparable接口,是可比较的
    • 没有带索引的方法,所以不能使用普通for循环遍历
    • 由于是Set集合,所以不包含重复元素的集合
      例 存储整数并遍历
package collection;

import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        //集合存储的是引用类型,int元素必须转为使用Integer
        TreeSet<Integer> treeSet = new TreeSet<Integer>();
        treeSet.add(10);
        treeSet.add(8);
        treeSet.add(11);
        treeSet.add(9);
        treeSet.add(10);
        for(Object i :treeSet){
            System.out.println(i);
        }
    }
}

2.5.7 自然排序Comparable的使用

  • 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
  • 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
package collection;

import java.util.TreeSet;
/*
    存储学生对象并遍历,创建集合使用无参构造方法
    要求:按照年龄从小岛大排序,年龄相同时,按照姓名的字母顺序排序
 */
public class TreeSetDemo1 {
    public static void main(String[] args) {
        TreeSet<Student> students = new TreeSet<>();
        Student s1 = new Student("xiaoming",22);
        Student s2 = new Student("xiaohong",20);
        Student s3 = new Student("lanlan",22);
        Student s4 = new Student("xiaoming",20);
        Student s5 = new Student("xiaoming",20);

        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);
        students.add(s5);

        for(Student s : students){
            System.out.println(s);
        }
    }
}
//////////////////////////////
package collection;

import java.util.Objects;

public class Student implements Comparable<Student>{
    private String name;
    private  int age;

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

    public Student() {
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name=‘" + name + ‘\‘‘ +
                ", age=‘" + age + ‘\‘‘ +
                ‘}‘;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student student = (Student) o;
        return Objects.equals(getName(), student.getName()) && Objects.equals(getAge(), student.getAge());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }

    /*
     返回是0 认为是相等的不添加
     返回是-1 降序,后添加的放在前面
     返回是1 升序,后添加的放在后面
     可以通过让返回值一直视-1/1/0 测试出来
     */
    @Override
    public int compareTo(Student o) {
    /*      自己写的没有老师写的精妙,int直接相减就可以比较
    if(this.age.compareTo(o.age)<0){
            return -1;
        }
        if(this.age.equals(o.age)){
            return (this.name.compareTo(o.age));
        }

        return 1;
        */
        int num = this.age-o.age;
        //采用三元运算符,只有在年龄相等时才继续比较名字
        int num2 = num==0? this.name.compareTo(o.name):num;
        return num2;
    }
}

输出:
Student{name=‘xiaohong‘, age=‘20‘}
Student{name=‘xiaoming‘, age=‘20‘}
Student{name=‘lanlan‘, age=‘22‘}
Student{name=‘xiaoming‘, age=‘22‘}

2.5.8 比较器Comparator的使用

  • TreeSet集合存储自定义对象,带参构造方法使用比较器对元素排序
  • 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1, T o2)方法
  • 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
    例 使用比较器实现有序插入Student元素到TreeSet集合中
package collections;

import java.util.Comparator;
import java.util.TreeSet;

/*
 * create TreeSet using constructor with parameter
 */
public class TreeSetDemo {

	public static void main(String[] args) {
		//在一开始创建TreeSet的时候指定了比较器
                //比较器利用匿名内部类实现,直接new Comparator接口
		TreeSet<Student> students = new TreeSet<Student>(new Comparator<Student>(){

			@Override
			public int compare(Student s1, Student s2) {
				int num = s1.getAge()-s2.getAge();
				int num2 = num == 0? s1.getName().compareTo(s2.getName()):num;
				return num2;
			}
			
		});
		Student s1 = new Student("xiaoming",22);
	    Student s2 = new Student("xiaohong",20);
	    Student s3 = new Student("lanlan",22);
	    Student s4 = new Student("xiaoming",20);
	    Student s5 = new Student("xiaoming",20);

	    students.add(s1);
	    students.add(s2);
	    students.add(s3);
	    students.add(s4);
	    students.add(s5);

	    for(Student s : students){
	        System.out.println(s);
	    }
	}
}
///////////////////////////////
package collections;

public class Student {
	private String name;
	private int age;
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	
	public Student() {
		super();
		// TODO Auto-generated constructor stub
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}

}

例2 成绩排序,按照总分从高到低排序
排序时注意主要条件和次要条件,当主要条件总分相同时,要比较其他科目如语文分数,当科目相同时要比较名字,目的是确保不同人都能添加到集合里
(有重名的用学号解决)
例 3 存储10个0到20间不重复的随机数
分析不重复采用set集合,不需要排序,直接用hashSet
随机数用 Random nextInt(20)+1限制范围

package collections;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class TreeSetDemo1 {

	public static void main(String[] args) {

		Set<Integer> set = new HashSet<Integer>();
                //创建随机数生成器
		Random r = new Random();
		
		while(set.size()<10){
			int number = r.nextInt(20)+1;
			set.add(number);
		}
		for(Integer i: set){
			System.out.println(i);
		}
	}

}

注: 内容是bilibili黑马程序员笔记

常用类,集合 一

原文:https://www.cnblogs.com/drying-net/p/14628522.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!