是否可以检查对象是数组还是带有一个子句的集合?我正在努力实现的目标:
假设数组实现Iterable,并假定对象foo
可以是数组或集合,我想使用如下代码片段:
if (foo instanceof Iterable) {
for (Object f : (Iterable) foo) {
// do something with f
}
}
不幸的是,不能将数组强制转换为Iterable。它也没有实现Collection。是否还有其他可能像上述那样在一个循环中处理这两者?当然不是使用if-else if-clause和两个循环(这不太好)。
编辑:针对这些答案。我知道isArray()方法,但是在这种情况下,强制转换
...
for (Object f : (Iterable) foo) {
...
将失败。遗憾的是,代码冗余,因为尽管foreach循环可同时用于Collections和Arrays,但我不得不使用两个循环。
答案 0 :(得分:18)
关于检查Class<?> fooClass = foo.getClass();
boolean isArrayOrCollection = Collection.class.isAssignableFrom(fooClass) ||
Object[].class.isAssignableFrom(fooClass);
是集合还是数组的条件:
Class#isAssignableFrom
可能会派上用场。
Object[].class.isAssignableFrom(fooClass)
我合理地假设您不会在原始数组上对其进行测试,因为您有仅适用于包装器类的集合。
我想您可以安全地将fooClass.isArray()
替换为boolean isArrayOrCollection = Collection.class.isAssignableFrom(fooClass) ||
fooClass.isArray();
class Test {
public static void main(String[] args) {
Predicate<Class<?>> p = c -> Collection.class.isAssignableFrom(c) ||
c.isArray();
System.out.println(p.test(new int[0].getClass()));
System.out.println(p.test(new Integer[0].getClass()));
System.out.println(p.test(Collections.emptyList().getClass()));
System.out.println(p.test(Collections.emptySet().getClass()));
System.out.println(p.test(Collections.emptyMap().getClass()));
}
}
,它也适用于原始数组类。
我已经进行了一次小“测试”
true
true
true
true
false
Collection
关于将同时在两个数组和两个数组上运行的通用循环 集合:
您只是不能编写准确的结构来处理此问题:Iterable
(或Object[]
)和Object
几乎没有共同点({{1 }}作为普通的父代及其方法还不够)。
我认为构建自己的抽象是明智的,该抽象将以相同的方式处理集合和数组。没有特定的上下文,我可以简单地想到两个子类,每个子类定义应如何迭代其 source (集合或数组)。然后,对接口进行编程将有助于平均管理它们。
一个非常简化的示例是:
interface Abstraction<T> {
void iterate(Consumer<? super T> action);
static <T> Abstraction<T> of(Collection<T> collection) {
return new CollectionAbstraction<>(collection);
}
static <T> Abstraction<T> of(T[] array) {
return new ArrayAbstraction<>(array);
}
static IntArrayAbstraction of(int[] array) {
return new IntArrayAbstraction(array);
}
}
class CollectionAbstraction<T> implements Abstraction<T> {
Collection<T> source;
public CollectionAbstraction(Collection<T> source) {
this.source = source;
}
@Override
public void iterate(Consumer<? super T> action) {
source.forEach(action);
}
}
class ArrayAbstraction<T> implements Abstraction<T> {
T[] source;
public ArrayAbstraction(T[] source) {
this.source = source;
}
@Override
public void iterate(Consumer<? super T> action) {
for (T t : source) {
action.accept(t);
}
}
}
class IntArrayAbstraction implements Abstraction<Integer> {
int[] source;
public IntArrayAbstraction(int[] source) {
this.source = source;
}
@Override
public void iterate(Consumer<? super Integer> action) {
for (int t : source) {
action.accept(t);
}
}
}
class Test {
public static void main(String[] args) {
Abstraction.of(new Integer[] {1, 2, 3}).iterate(System.out::println);
Abstraction.of(Arrays.asList(1, 2, 3)).iterate(System.out::println);
Abstraction.of(new int[] {1, 2, 3}).iterate(System.out::println);
}
}
我相信上述方法相当通用。您不必依赖特定来源的迭代方式,可以有选择地修改它们。
答案 1 :(得分:10)
其他答案都在努力回答原始标题问题:
数组和集合是否有公共接口或超类?
但是您真正的问题在体内:
还有没有其他可能像上述那样在一个循环中处理这两个问题?
答案是:否,无法编写一个遍历集合和数组的for
循环。
您可以跳过一堆箍将数组转换为列表,但是几乎可以肯定,与仅编写两个(或多个)循环相比,最终会陷入更大的混乱。调用getClass().isArray()
会告诉您所拥有的内容,但是如果没有某种类型的转换,您仍然无法使用它。 Arrays.asList()
不适用于图元数组。
答案 2 :(得分:6)
根据您要执行的操作,您可能需要实现两种类似的方法:
public <T> void iterateOver(List<T> list) {
// do whatever you want to do with your list
}
public <T> void iterateOver(T[] array) {
this.iterateOver(Arrays.asList(array));
}
或者甚至为此有一个接口:
interface ExtendedIterableConsumer<T> {
public void iterateOver(List<T> list);
public default void iterateOver(T[] array) {
this.iterateOver(Arrays.asList(array));
}
我不确定这是否对您有帮助,因为您似乎已经将某个对象放在某个变量中。但是,如果您可以将此问题再上一层解决,那么它可能会很有用。
答案 3 :(得分:5)
您可以使用isArray()
中的Class
方法来检查对象是否为数组
if (foo != null && (foo.getClass().isArray() || foo instanceof Collection<?>)){
}
编辑:
就迭代这个foo
对象而言,没有简单的解决方案。但是,您可以尝试执行以下操作:
private void iterate(@NotNull Object foo) {
if (foo instanceof Collection<?>) {
for (Object o : ((Collection<?>) foo)) {
chandleYourObject(o);
}
}
if (foo.getClass().isArray()) {
if (foo.getClass().isPrimitive()) {
checkPrimitiveTypes(foo);
}
if (foo instanceof Object[]) {
for (Object o : (Object[]) foo) {
chandleYourObject(o);
}
}
}
}
private void checkPrimitiveTypes(Object foo) {
if (foo instanceof int[]) {
for (int i : (int[]) foo) {
}
}
//And the rest of primitive types
}
private void chandleYourObject(Object o ){
//What you want to do with your object
}
答案 4 :(得分:4)
您可以为此编写辅助方法:
@SuppressWarnings("unchecked")
public static <E> void forEach(Object arrayOrIterable, Consumer<? super E> action) {
Objects.requireNonNull(arrayOrIterable);
if (arrayOrIterable instanceof Iterable) {
for (Object o : (Iterable<?>) arrayOrIterable) {
action.accept((E) o);
}
} else if (arrayOrIterable.getClass().isArray()) {
int length = Array.getLength(arrayOrIterable);
for (int i = 0; i < length; i++) {
action.accept((E) Array.get(arrayOrIterable, i));
}
} else {
throw new IllegalArgumentException("not an array nor iterable: " + arrayOrIterable.getClass());
}
}
第二个分支利用提供帮助方法的java.reflect.Array
类(可能很慢)来获取数组的length
和给定索引的元素。
您可以这样称呼它:
int[] ints = {1, 2, 3, 4};
forEach(ints, (Integer i) -> System.out.println(i));
List<Integer> ints = Arrays.asList(1, 2, 3, 4);
forEach(ints, (Integer i) -> System.out.println(i));
由于泛型的性质,此方法可能会抛出ClassCastException
,例如这个电话:
int[] ints = {1, 2, 3, 4};
forEach(ints, (String s) -> System.out.println(s));
会导致:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
答案 5 :(得分:4)
数组是对象:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-10.html
AbstractCollections还扩展了对象:
https://docs.oracle.com/javase/8/docs/api/java/util/AbstractCollection.html
是的,有一个通用的超类,但是不幸的是,这实际上并不能帮助您。
我建议您最好使用:
List<> someList = Arrays.asList(sourceArray)
这会将您的数组转换为实现Iterable的集合。当然,您将需要事先确定初始对象是数组还是集合,并且仅在它是数组时才调用上面的对象,这里有一些实现此目的的选项:
boolean isArray = myArray.getClass().isArray();
boolean isCollection = Collection.class.isAssignableFrom(myList.getClass());