如何制作一个真实的"番石榴中Date对象的不可变列表

时间:2014-08-12 01:37:09

标签: java date guava immutability

如何使用guava创建一个不可变的日期列表(java.util.Date)?

我有这个片段:

    Date date = new GregorianCalendar(2014, 4, 1).getTime();    

    // doesn't work: 
    // List<Date> immutableList = ImmutableList.of(date);
    // doesn't work either:
    List<Date> immutableList = ImmutableList.copyOf(new Date[] { date });

    date.setMonth(3);

    System.out.println("immutableList has: " + immutableList.get(0));

而我想让它成为一个真正的&#34;不可变列表,以便在更改date对象时不会更改第0个元素。

4 个答案:

答案 0 :(得分:7)

正如您已经发现的那样,使用不可变列表可以保护列表不被修改,但不会保护列表中包含的元素。为此,元素本身必须是不可变的。

不幸的是,它无法创建不可变的java.util.Date对象。您可以考虑几种方法:

  1. 如果您正在使用Java 8,请使用JSR-310引入的不可变日期/时间值类。您可以找到介绍性文章here
  2. 如果您不使用Java 8,最好的办法是使用Joda Time library中的不可变类。
  3. 编写自己的日期包装类,以保护包装日期对象不被修改。
  4. 虽然java.util.Date类型的对象是可变的,但java.lang.Long类型的对象是不可变的 因此,您可以创建一个Long个对象的不可变列表来存储从调用Date.getTime()获得的long值,并在检索时使用Date(long date)构造函数动态构造新的日期对象从列表中。

答案 1 :(得分:1)

tl; dr

  • 不需要Guava。内置功能就足够了。
  • Java 9和10中的
  • List.ofList.copyOf分别产生不可修改的List实现。
  • 使用现代的 java.time 类,切勿使用DateCalendar
    • java.time 对象是不可变的,其状态不能更改。

示例:

List.copyOf(                                          // Produce an unmodifiable `List` based on elements drawn from another `Collection` in iteration-order. 
    LocalDate.of( 2014 , Month.MARCH , 1 )            // Modern class to represent a date-only value, without time-of-day and without time zone.
    .datesUntil(                                      // Generates a stream of `LocalDate` objects, incrementing one day at a time. 
        LocalDate.of( 2014 , Month.APRIL , 1 )        // The `LocalDate` class is designed to produce immutable objects, with state that can be read but not altered (not “mutated”). 
    )                                                 // Returns a `Stream<LocalDate>`.
    .collect(                                         // Processes the series of dates coming from the stream.
        Collectors.toCollection( ArrayList :: new )   // Creates a `List`, and adds the series of dates to that list. 
    )                                                 // Returns a `List<LocalDate>`. 
)                                                     // Returns another `List<LocalDate>` whose elements cannot be added, dropped, or replaced from the collection.
.toString()                                           // Generates a `String` with text representing the value of each contained `LocalDate` object.
  

[2014-03-01、2014-03-02、2014-03-03、2014-03-04、2014-03-05、2014-03-06、2014-03-07、2014-03- 08、2014-03-09、2014-03-10、2014-03-11、2014-03-12、2014-03-13、2014-03-14、2014-03-15、2014-03-16, 2014-03-17、2014-03-18、2014-03-19、2014-03-20、2014-03-21、2014-03-22、2014-03-23、2014-03-24、2014- 2014年3月25日,2014-03-26、2014-03-27、2014-03-28、2014-03-29、2014-03-30、2014-03-31]

不需要番石榴

Google Guava库在许多方面都非常有用。但是,不再需要为一系列日期实现只读数据结构的目标。 Java现在具有支持此功能的内置功能。<​​/ p>

java.time

现代方法使用 java.time 类代替了可怕的旧日期时间类,例如DateCalendar

LocalDate

LocalDate类表示没有日期和时区的仅日期值。

您的问题不清楚。似乎您想要的是上个月的日期列表。

LocalDate stop = LocalDate.of( 2014 , Month.APRIL , 1 ) ;  // Sane numbering: `2014` is the year 2014, and 1-12 = Jan-Dec.
LocalDate start = stop.minusMonths( 1 ) ;  

您可以使用Java Streams工具来生成一系列LocalDate对象。首先创建一个Stream< LocalDate >

Stream< LocalDate > streamOfLocalDates = start.datesUntil( stop ) ;

然后,将从该流发出的一系列LocalDate对象收集到List集合中。

List< LocalDate > localDates = 
    streamOfLocalDates.collect( 
        Collectors.toCollection( ArrayList::new ) 
    )
;

上面产生的List是可修改的,这意味着我们可以添加,删除或替换其元素。为了防止对List进行此类更改,请使用Java 10及更高版本中的新List功能,生成另一个List,这是不可修改的List.copyOf

List< LocalDate > localDatesUnmodifiable = 
    List.copyOf( 
        localDates 
    ) 
;

我们在这里设有两个级别的保护措施,以防止数据发生更改。

  • 使用List.copyOf可防止您在列表中添加,删除或替换元素。它并不能阻止您修改存储在每个元素中的对象的内部状态。
  • java.time 类使用immutable objects pattern的事实可以保护您免受元素对象状态变化的影响。

因此List.copyOf( List< LocalDate > )的元素或内容(完全是只读的数据结构)都无法修改。


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 2 :(得分:0)

没办法。如果您确实想使用Date,则必须克隆每个get()结果以保护存储的值。你还必须克隆你输入的日期,除非你确定它们之后不会改变。

您可以做的最好的事情是切换到某种不可变的数据类型。否则,你必须包装所有访问,这是一个真正的痛苦。您可以将List设置在long[]上(这可能比调整列表更不容易出错,因为不会错误地发布存储对象)。

答案 3 :(得分:0)

如果您别无选择,只能使用Date对象,那么您始终可以返回date对象的副本。这将使您的对象不可变。

public final class ImmutableDates{
    private final List<Date> dates;
    // other final fields 

    // constructor with final dates and other final fields as argument

    // don't expose setter

    // always create and return a new list populated with new dates
    public List<Date> getDates() {
        List<Date> newDates = dates.stream().map(p -> new Date(p.getTime())).collect(Collectors.toList());
        return newDates;
    }
}

始终创建并返回填充有新日期的新列表