首页 > 从guava的一个“坑”说起
头像
stevenniu
发布于 2020-12-19 16:18
+ 关注

从guava的一个“坑”说起

杂谈之内卷

最近在几个技术交流群经常看到大家讨论开发行业内卷,什么是内卷呢?一句话,大家越来越努力,越来越累,但是幸福感和收获并没有增加,甚至实际上是下降了。

举个大城市西安的例子

为了让小孩能上更好的小学,家长都发疯一样买学区房,但是好学校一年的招生名额是有限的,不管学区房有多贵,并不能增加招生名额。比如某名校一年收250个小学生,就算周边学区房涨价到3万一平米,还是一年收250个学生,但家长们为了让孩子入学,不得不接受3万一平米的单价,去买一套老破小居住,说不定还要把原先的大房子卖掉,因为限购了,名下只能有一套房。

更可怕的是,学区房政策是会变的,说不定每年都变,所以你就算买了学区房也不一定能够顺利上学。比如,周边适龄儿童有400个人,可是招生名额还是只有250,那就意味着肯定有150人无法入学。那怎么办呢?学校就搞了个排名,有本地户籍且人户一致的优先入学,有本地户籍人户不一致的排第二,没有本地户籍但有房产证的排第三……如果以上这些都卡不住你怎么办呢?很简单,多校划片,最终能上哪个学校,电脑随机抽签。

在这里插入图片描述

但事实上考大学一样是内卷游戏,内卷给人一种恐慌的感觉,大家在恐惧的驱使下,竭尽所能争夺资源,可是资源有限,大家越是争夺,资源损坏越大,甚至无序的竞争会带来毁灭性结局。可是你不争,就被别人得了便宜,被人骂SB。

社会发展到今天,凭现在的生产力,足以让所有人过上衣食无忧的生活。但是由于分配问题,由于资本的压榨本质,使得大部分人并没有因为生产力和科技的发展获得相应的幸福感。内卷的结果,是资本可以获得更加廉价的劳动力,资本增值更快。但是劳动力本身却更苦了。尤其是在这个科技和知识是第一生产力的时代,内卷并不利于社会进步。因为知识生产需要一个比较宽松的社会环境。社会福利越好,知识生产反而会越佳。那种残酷竞争所带来的不安全感,实际上是不利于科技进步的。内卷状态下的知识生产者,是非常焦虑的。相应的,知识学习者也是极度焦虑,这样的学习效果其实并不好。这更何况,这种过分的竞争极大的降低了人们的安全感和幸福感。……我所想象的理想世界是,每个人可以在解决温饱的前提下,做自己喜欢做的工作,焕发出最大的工作热情,从而促进社会进步。当然,这只是理想。

我不知道如何改变内卷的命运,但可以尽量在内卷的前头早做准备,2020年虽然是非常艰难的一年,但相对于未来而言,反倒可能是最容易的一年了,因为将来随着内卷的深入,一定会越来越困难的,别到时候又后悔错过了2020年的机会。种一个颗树最好的时间一个是十年前,另一个就是现在。机会也永远留给有准备的人,你不去学习不去进步的话,到时候又要做负责接盘的下家了。

正文

最近,团队里边一个兄弟突然叫我:快来看,有个奇怪的事情,无法解释…
跑过去一看,是这么一段代码:

 private static class Person {
        private int age;
        private String name;

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

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

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

    @Test
    public void test_collections2_filter() {
        Person lxy = new Person(35, "lxy");
        Person xhf = new Person(34, "xhf");
        Person nws = new Person(31, "nws");
        List<Person> names = Lists.newArrayList(lxy, xhf, nws);

        Collection<Person> personAgeOver30 = Collections2.filter(names, p -> p.age > 30);
        System.out.println(personAgeOver30);//[lxy:35, xhf:34, nws:31]

        nws.setAge(25);
        System.out.println(personAgeOver30);//[lxy:35, xhf:34]
    }

确实是比较奇怪,personAgeGt30中的元素怎么会少了一个呢?
本着任何表现奇怪的程序都有其背后原因的指导思想,打开了Guava Collections2类的源代码。
其实,源代码的注释已经解释得非常清楚了:returned collection is a live view of {@code unfiltered};
changes to one affect the other.

/**
   * Returns the elements of {@code unfiltered} that satisfy a predicate. The returned collection is
   * a live view of {@code unfiltered}; changes to one affect the other.
   *
   * <p>The resulting collection's iterator does not support {@code remove()}, but all other
   * collection methods are supported. When given an element that doesn't satisfy the predicate, the
   * collection's {@code add()} and {@code addAll()} methods throw an {@link
   * IllegalArgumentException}. When methods such as {@code removeAll()} and {@code clear()} are
   * called on the filtered collection, only elements that satisfy the filter will be removed from
   * the underlying collection.
   *
   * <p>The returned collection isn't threadsafe or serializable, even if {@code unfiltered} is.
   *
   * <p>Many of the filtered collection's methods, such as {@code size()}, iterate across every
   * element in the underlying collection and determine which elements satisfy the filter. When a
   * live view is <i>not</i> needed, it may be faster to copy {@code Iterables.filter(unfiltered,
   * predicate)} and use the copy.
   *
   * <p><b>Warning:</b> {@code predicate} must be <i>consistent with equals</i>, as documented at
   * {@link Predicate#apply}. Do not provide a predicate such as {@code
   * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. (See {@link
   * Iterables#filter(Iterable, Class)} for related functionality.)
   *
   * <p><b>{@code Stream} equivalent:</b> {@link java.util.stream.Stream#filter Stream.filter}.
   */

Collections2.filter方法返回的只是原有列表的一个视图。所以:改变被过滤列表会影响过滤后列表,反之
亦然。并且我们从这段文字中还能get到下面几个注意事项:

  1. 过滤后列表的迭代器不支持remove()操作

  2. add不符合过滤条件的元素,会抛出IllegalArgumentException

  3. 当过滤后列表中的元素不再满足过滤条件时,会影响到已经过滤出来的列表出于程序员的本能,决定还是看下源码心里更踏实。核心源码如下:

    1. add方法:
      public boolean add(E element) {
          //不符合过滤条件抛IllegalArgumentException的原因
          checkArgument(predicate.apply(element));
          return unfiltered.add(element);
      }
      不支持通过迭代器删除的原因:
      public abstract class UnmodifiableIterator<E> implements Iterator<E> {
      /** Constructor for use by subclasses. */
      protected UnmodifiableIterator() {}
      /**
    
    * Guaranteed to throw an exception and leave the underlying data unmodified.
     *
    * @throws UnsupportedOperationException always
    * @deprecated Unsupported operation.
     */
     @Deprecated
     @Override
     public final void remove() {
         throw new UnsupportedOperationException();
     }
     打印结果变化的原因:
     public String toString() {
         Iterator<E> it = iterator();
         if (! it.hasNext())
             return "[]";
         StringBuilder sb = new StringBuilder();
         sb.append('[');
         for (;;) {
             E e = it.next();
             sb.append(e == this ? "(this Collection)" : e);
             if (! it.hasNext())
             return sb.append(']').toString();
             sb.append(',').append(' ');
         }
     }
     @Override
     public Iterator<E> iterator() {
         return Iterators.filter(unfiltered.iterator(), predicate);
     }

    问题已经弄清楚了,怎么修改这个问题呢?
    在这里插入图片描述

方案一(可行):

干脆不用guava,

  List<Person> names = Lists.newArrayList(lxy, xhf, nws);
      ArrayList<Person> persons = new ArrayList<>();
      for (Person p : names) {
      if(p.age > 30) {
          persons.add(p);
      }
  }

方案二(可行):

用个容器再包装一下:

  Collection<Person> personAgeGt30 = new ArrayList<(Collections2.filter(names, p -> p.age > 30));

方案三(可行,改用Java8的过滤器):

  List<Person> personAgeGt30 = names.stream().filter((Predicate<Person>) p -> p.age >30).collect(Collectors.toList());

方案四(可行,改用Guava的连贯接口,IDEA编辑器会提示你替换成Java8 API)

ImmutableList<Person> personAgeGt30 = FluentIterable.from(names).filter(p -> p.age > 30).toList();

上述方案中,支持java8的生产环境推荐方案三,不支持java8的生产环境推荐方案二。

总结

其实,Java语言中类似的坑还有很多,比如:

  • 1.Arrays.asList()生成的列表是不可变的。
  • 2.subList生成的子列表,对原列表元素的修改,会导致子列表的遍历、增加、删除抛出ConcurrentModificationException,
  • 3.subList对子列表的修改会影响到原列表数据
  • 4.修改Map的keySet()方法和values()生成的容器,都会影响Map本身。

总之,使用任何API之前多看看源码,至少看看源码的注释。

全部评论

(3) 回帖
加载中...
话题 回帖

相关热帖

近期精华帖

热门推荐