【学习笔记】8

2、set集合

2.1概述

不包含重复集合的集合。

没有带索引的方法,所以不能使用普通for循环遍历。

Set<String> set = new HashSet<String>();
        set.add("huhfn");
        set.add("my");
        set.add("dear");
        set.add("my");
        for(String s:set){
            System.out.println(s);
        }
        out:
huhfn
my
dear

2.2哈希值

哈希值:时jdk根据对象的地址或者字符串或者数字算出来的int类型。

hashCode():返回对象的哈希值。(object类中)。

Student s1 = new Student("z",5);
        System.out.println(s1.hashCode());//1324119927
        Student s2 = new Student("z",5);
        System.out.println(s2.hashCode());//990368553

同一个对象的哈希值时相同的。

不同对象的哈希值再默认情况下时不同的。

通过方法重写可以实现哈希值时相同的。

2.3 hashset概述

java.util包下。实现了set接口,对集合的顺序不做保证。底层数据结构时哈希表。没有带索引的方法,不包含重复元素的集合。

 public static void main(String[] args) {
        HashSet<String> hs = new HashSet<String>();
        hs.add("hello");
        hs.add("world");
        hs.add("java");
        for(String s:hs){
            System.out.println(s);
        }
        /*
        world
java
hello
         */
    }
}

2.4 为啥不重复呢?

Alt + 7打开structure窗口。

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);
    }
    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;
            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;
    }
​

&&短路,前面如果false则后面都不会运行。

要保证元素唯一性,需要重写hashcode和equis那个。

2.5哈希表

hashset()构建一个新集合,默认初始容量为16.0-15

hello:99162322%16 =2. 首先将hello放到2的位置。

world:113318802%16 = 2 。 与hello同一个位置,与hello比较首先比较哈希值,world存储进来了。

java:3254818%16 =2 .也在2,同以前的比较哈希值不同,java存储进来了。

world:113318802%16 = 2 .与第一个比较哈希值不同,比较第二个world发现与第二个哈希值相同。哈希值相同情况下比较内容,内容相同,说明元素重复,不存储。

2.6实例,重写hashset和equals方法就保证了唯一性

创建一个学生对象,存储多个学生对象,使用程序实现控制台遍历改集合。

HashSet<Student> hs = new HashSet<Student>();
        Student s1 = new Student("z",1);
        Student s2 = new Student("a",2);
        Student s3 = new Student("b",3);
        Student s4 = new Student("b",3);
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);
        for(Student s :hs){
            System.out.println(s.getAge()+","+s.getName());
        }
 当在student中没有重写hashset和equals方法时:
 输出:
 2,a
3,b
1,z
3,b
重写之后:直接
2,a
3,b
1,z
​

重写方法:制动生成就行。alt+insert,选择这两方法,直接next就行。

2.7 linkedhashset

这个可以保证元素的存储和取出顺序是一致的。

哈希表和链表实现的是set接口,具有可预测的迭代次序。

由哈希表保证元素的唯一,也就是说没有重复的元素。

联系:存储字符串并遍历

LinkedHashSet<String> ls = new LinkedHashSet<String>();
        ls.add("sd");
        ls.add("hj");
        ls.add("df");
        ls.add("df");
        for (String s : ls){
            System.out.println(s);
        }
        输出:
        sd
hj
df

2.7 treeset集合的概述和特点

间接实现了set接口。

元素有序。(按照一定的规则进行排序)

可以实现自然排序,或者comparator可以根据比较器排序。

用哪一种排序,根据构造方法。无参构造根据自然排序进行排序。带参构造:TreeSet(Comparator)根据指定的比较器进行排序。

没有带索引的方法。不能使用普通的for循环遍历。

练习:存储整数并遍历。

public static void main(String[] args) {
        TreeSet<Integer> ts = new TreeSet<Integer>();
        ts.add(1);
        ts.add(20);
        ts.add(90);
        ts.add(5);
        for (Integer i :ts){
            System.out.println(i);
        }
    }
输出:
1
5
20
90
//自动排序

TreeSet<Integer> ts = new TreeSet<Integer>();

!!!!注意!!!《里面》需要写的是其包装类型,比如int不要直接写int,要写Integer。

2.8 自然排序Comparable的使用

存储学生对象并遍历,使用无参构造。

要求:按照年龄从小到大。年龄相同时,按照姓名的字母顺序排序。

!!!报错了!!!!

public static void main(String[] args) {
        TreeSet<Student> ts = new TreeSet<Student>();
        Student s1 = new Student("z",1);
        Student s2 = new Student("a",2);
        Student s3 = new Student("b",3);
​
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        for (Student s:ts){
            System.out.println(s.getAge()+","+s.getName());
        }// java.lang.ClassCastException: class com.zy2.Student cannot be cast to class java.lang.Comparable
    }

Student要实现接口Comparable《Student》,该程序认为大的应该放后面。根据返回值认为,返回1认为下一个比前一个大。-1认为下一个比前一个小,现在的放前面。

需要重写compareTo....。。因为返回的是0,所以它会认为s2和s1是一样的。

@Override
    public int compareTo(Student o) {
        return 0;
    }
如果这么写,只能存储一个元素。
输出:
1,z

!!!!!!!return 1.就是按照存储的顺序来输出的。因为返回的是正数,所以它认为s2比s1大。

@Override
    public int compareTo(Student o) {
        return 1;
    }
}
1,z
2,a
3,b

负数,s2比s1小认为,

@Override
    public int compareTo(Student o) {
        return -1;
    }
}
3,b
2,a
1,z

下面这个this代表s2,s代表s1。如果正数,s2》s1,正数往后放,负数往前放。

第一个输入的7,第二个2,return 负数,往前放,变成了2-7.

第三个输入3,3-2=正,所以往后放。

Student s1 = new Student("z",7);
        Student s2 = new Student("a",2);
        Student s3 = new Student("b",3);
@Override
    public int compareTo(Student s) {
        int num = this.age-s.age;
        return num;
    }
2,a
3,b
7,z

String本身就继承Compara接口。

这是最终实现的代码。

Student s1 = new Student("z",7);
        Student s2 = new Student("a",2);
        Student s3 = new Student("b",3);
        Student s4 = new Student("h",3);
        Student s5 = new Student("h",3);
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
@Override
    public int compareTo(Student s) {
        int num = this.age-s.age;
        int n = num==0?this.name.compareTo(s.name):num;
        return n;
    }

总结:

无参构造使用的是自然排序的方法对元素惊醒重构的。

自然排序就是让元素所属的类实现Comparable接口,重写comparaTo(E e)方法

重写方法的时候,看要求。

2.9 Comparator比较器排序

联系要求跟上面一样。

存储学生对象并遍历,使用无参构造。

要求:按照年龄从小到大。年龄相同时,按照姓名的字母顺序排序。

public int compare(Student s1, Student s2)里面的前面那个s1是当前输入的值,s2是之前输入的数值

​
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {//匿名啥啥类来着
            @Override
            public int compare(Student s1, Student s2) {
                int num = s1.getAge()- s2.getAge();
                int n = num==0?s1.getName().compareTo(s2.getName()):num;
                return n;
            }
        });
        Student s1 = new Student("z",86);
        Student s2 = new Student("a",34);
        Student s3 = new Student("b",99);
        Student s4 = new Student("h",3);
        Student s5 = new Student("h",3);
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        for (Student s:ts){
            System.out.println(s.getAge()+","+s.getName());

成绩排序练习

TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int s1 = o1.getChinese()+ o1.getMath();
                int s2 = o2.getChinese()+ o2.getMath();
                int num = s2-s1;
                int n = num==0? o1.getChinese() - o2.getChinese():num;
                int n2 = n==0?o1.getName().compareTo(o2.getName()):n;
                return n2;
            }
        });
        Student s1 = new Student("1",20,20);
        Student s2 = new Student("2",10,20);
        Student s3 = new Student("3",80,20);
        Student s4 = new Student("4y",5,20);
        Student s5 = new Student("5y",4,21);
        Student s6 = new Student("adf",80,20);
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        for (Student s :ts){
            System.out.println(s.getChinese()+","+s.getMath()+","+s.getName());
        }
80,20,3
80,20,adf
20,20,1
10,20,2
4,21,5y
5,20,4y

2.10 不重复的随机数

练习:编写一个程序,获取1-20之间的随机数,要求随机数不能重复,并在控制台输出。

1、创建set集合对象

2、创建随机数对象

3、判断集合长度是不是小于10

是:产生一个随机数。

回到3继续。

4、遍历集合。

用hashset方法顺序随意。Treeset是输出是有顺序的。从小到大。

public class demo1 {
    public static void main(String[] args) {
//        Set<Integer> set = new HashSet<Integer>();
        Set<Integer> set = new TreeSet<Integer>();
        Random r = new Random();
        while (set.size() < 10) {
            int n = r.nextInt(20) + 1;
            set.add(n);
        }
        for (Integer i : set) {
            System.out.println(i);
        }
    }
}

3、泛型

3.1概述

泛型:是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译的时候检测到非法的类型。

他的本质是参数化类型,也就是所操作的数据类型被指定为一个参数

顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型。

这种参数类型可以i用在类、方法和接口中。分别称为泛型类,泛型方法、泛型接口

定义格式:

《类型》:指定一种类型的格式,这里的类型可以堪称是形参

《类型1,类型2.。。》:指定多种类型,多种类型之间用逗号隔开,这里的类型可以堪称是形参

将来具体调用的时候给定的类型可以看成时实参,并实参的类型时引用数据类型。

Collection<,,,,,,> c = new ......这里面的<>的里面就是泛型。

Collection c = new ArrayList();//没有给泛型
        c.add("dcdv");
        c.add("bhb");
        c.add("kj");
        c.add(10);//java.lang.ClassCastException:
        for (Object s:c){
            System.out.println((String)s);
        }

好处:

把问题给提前了,在编译的时候就一定要改。把运行时候的问题提前到了编译的时候。
不需要加强制类型转换了。

3.2泛型类

定义格式

修饰符class 类名<类型>{ }

public class Gen<T>{}

类型一般用T,E,K,V表示泛型。

Gen<String> g1 =new Gen<String>();
        g1.setT("mhfgbhd");
        System.out.println(g1.getT());
        Gen<Integer> i = new Gen<Integer>();
        i.setT(52);
        System.out.println(i.getT());
mhfgbhd
52
public class Gen<T> {//泛型类
    private T t;
​
    public T getT() {
        return t;
    }
​
    public void setT(T t) {
        this.t = t;
    }
}
​

3.3 泛型方法

格式:

修饰符<类型> 返回值

泛型类改进
public class fangfa<T> {
    public void show(T t){
        System.out.println(t);
    }
}
泛型方法改进
public class fangfa {
    public <T> void show(T t){
        System.out.println(t);
    }
}
fangfa<String> s = new fangfa<String>();
        s.show("djfbh");
        fangfa<Integer> i = new fangfa<Integer>();
        i.show(52);
        fangfa<Boolean> b = new fangfa<Boolean>();
        b.show(true);
 泛型方法改进
fangfa s = new fangfa();
        s.show("djfbh");
        s.show(52);
        s.show(true);
djfbh
52
true

3.4 泛型接口

格式:

修饰符 interface 接口名<类型>{ }

jiekou<String> i = new jiekou<String>();//psvm
        i.show("df");
        jiekou<Integer> g = new jiekou<Integer>();
        g.show(52);
public class jiekou<T> implements Geni<T>{//接口实现类
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}
​
public interface Geni<T> {//接口
    void show(T t);
}

3.5 类型通配符

为了表示各种泛型list的父类,可以使用类型通配符

类型通配符:<?>

List<?>:表示元素类型位置的List,他的元素可以匹配任何类型

这种带通配符的List仅仅表示它是任何泛型的父类,并不能把元素添加到其中。

如果不希望List<?>是任何泛型的父类,只希望它代表某一类泛型List的父类,可以使用类型通配符的上限。

类型通配符的上限:<?extends 类型>

List<?extends Number>:他表示的类型是Number或者其子类

类型通配符的下限:<?super 类型>

List<?super Number>:它表示的类型是Number或者其父类

List<?> l1 = new ArrayList<String>();
        List<?> l2 = new ArrayList<Integer>();
​
//        List<? extends Number> l3 = new ArrayList<Object>();//错误
        List<? extends Number> l3 = new ArrayList<Number>();
        List<? extends Number> l4 = new ArrayList<Integer>();
        List<? super Number> l5 = new ArrayList<Number>();
        List<? super Number> l6 = new ArrayList<Integer>();//错误

3.6 可变参数

格式:

修饰符 返回值类型 方法名(数据类型... 变量名){}

  public static void main(String[] args) {
        System.out.println(sum(10, 20,20));
    }
    public static int sum(int... a) {//a把方法调用中封装的参数封装到数组中
        int sum = 0;
        for (int i : a) {
            sum += i;
        }
        return sum;
    }
    50
 public static void main(String[] args) {
        System.out.println(sum(10, 20,20));
    }
    public static int sum(int b,int... a) {
        int sum = 0;
        for (int i : a) {
            sum += i;
        }
        return sum;
    }
    40

使用:

Arrays工具类中有一个静态方法:

public static <T> List <T> asList(T... a):返回由指定数组支持的固定大小的列表。

返回的集合不能做增删操作,可以做修改操作。

List接口中有一个静态方法:

public static《E》 List《E》 of(E... elements):返回包含任意数量元素的不可变列表。

返回的集合不能做增删改操作

set接口中有一个静态方法:

public static<E>Set<E> of (E...elements):返回一个包含任意数量元素的不可变集合。

给元素不能给重复元素,返回的集合不能做增删,没有修改方法因为没有索引。

public static void main(String[] args) {
        List<String> l = Arrays.asList("hjdf","dkj","ooo");//返回由指定数组支持的固定大小的列表。
//        l.add("pppppp");//错误。因为返回的是一个固定大小的列表。
//        l.remove("df");//错误UnsupportedOperationException
        l.set(1,"pppppppppp");//可以
        System.out.println(l);
        List<String> df = List.of("df", "hhh", "ooooo", "yyy","yyy");//不可变列表。
//        df.add("sssss");//错误
//        df.remove("yyy");//错误
//        df.set(1,"jjjj");//错误UnsupportedOperationException
        System.out.println(df);
//        Set<String> s = Set.of("qqqq", "uuuuuu", "ppppp","ppppp");//错误,IllegalArgumentException非法或者不正确的参数
        //因为set集合不能有重复元素
        Set<String> s = Set.of("qqqq", "uuuuuu", "ppppp");
        s.add("kk");//错误
        s.remove("ppppp");//错误
        System.out.println(s);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值